$Cart['products'] Vs $Cart_Products

What is the difference between these?

Throughout stuff that touches on the cart, you see things like:

function fn_get_products_weight($cart, $cart_products, $type = 'S')
{
$weight = 0;
if (is_array($cart_products)) {
foreach ($cart_products as $k => $v) {
if ($type == 'S') {
if (fn_exclude_from_shipping_calculate($cart['products'][$k])) {
continue;
}
} ....
It kind of looks like we're keeping two copies of the same data. But obviously if the right choice is to do fn_exclude_from_shipping_calculate($cart['products'][$k]) instead of doing that on $v, they're not the same thing. Conversely, why would you not do the foreach on $cart['products'] here.

If you wanted to do that on $v, you would need to change the foreach() statement to read:

foreach ($cart_products as $k => &$v) {

which would then make $v be a pointer into (by reference) $cart_products[$k] rather than a copy of $cart_products[$k] (by value).

Right I can see how that's an alternate way of working with what's there, but my real question is more around the notion of why is there $cart_products, when $cart already has $cart['products'].

So for example in that block of code I pasted, they iterate on $cart_products yet they use $cart['products'] (which is similar enough to share the same key) in the function call.

You're correct in that it's less clear even though it references the same data.

I'm guessing it's a coding error but in this instance does not have any negative side effects since $k is the operative value.