At its heart, a correlated subquery is simply one that refers to data from the outer query.
These are (non-correlated) subqueries:
, (SELECT count(*) FROM sys.objects) AS TotalObjects
, (SELECT count(*) FROM sys.indexes) as TotalIndexes
The processing of each of these subqueries is independent of each other and the processing of the outer query, saving that the final results will not be returned before all queries are finished and merged.
These are correlated subqueries:
, (SELECT count(*) from sys.indexes I WHERE I.object_id = O.object_id) as Indexes
FROM sys.objects O
WHERE EXISTS (SELECT 1 from sys.indexes I WHERE I.object_id = O.object_id)
Now both subqueries are dependent upon information from the outer query, so (D) their processing will always be affected by the processing of the outer query.
However, depending on the nature of the data and the query, the query optimizer may or may not change the processing of the outer query. It could process the outer query as written, use the resultset to process the subquery, and merge and filter the results, or it could reorganize the outer query and subqueries into joins. In my example, the correlated subquery in the SELECT clause might not affect the outer query at all; the correlated subquery in the WHERE clause probably will.
So (D) is always true; (C) is possible but not necessarily true.