• Welcome to the new Internet Infidels Discussion Board, formerly Talk Freethought.

A Successor of C++?

As to value vs. reference, plain C does all function calls by value. This is OK for input of lightweight data, like integers or floats or characters, but not for heavyweight data or when one wants to return a modified value in an arg.

For that, C++ offers call by reference, with &, thus hiding a pointer. Thus, instead of transmitting a pointer, int *num, one does int &num, and if one has some heavyweight object that one does not want changed, something like const int &num.

Many features of C++ are designed to hide pointers, and also to avoid using the preprocessor. Declaring something const makes it unnecessary to define constants with the preprocessor, and template functions and classes also make a lot of preprocessor use unnecessary,. C++ also offers the keyword "inline", to specify inserting a function's code into its caller, something that offers the sort of speedup that one can get with a preprocessor function. All this C++ preprocessor banishment offers an additional nice feature: type safety.

C++ mostly uses static typing, something that may be annoying when coding, but something that enables catching a lot of bugs at compile time rather than run time.

That static typing is why Microsoft created TypeScript - a sort of type-safe front end for JavaScript.
Indeed. Debugging C code can be a lot easier than other languages. More effort at the front end, less effort at back end.
 
A system has a memory map and that gets into hardware. You can always use in line assembly code to access registers. Or you can write assembly code and link it to the C code.

A processor has a memory bus. Imagine a 16 bit bus. Memory locations of devices like a serial or internet chip are decoded by hardware.

For example a chip may have its select pin enabled when 0x1000 appears on the bus. The registers or memory in the chip are then accessed from the physical address on the bus.
R0 0x1000
R1 0x1001

and so on.

In C code I would declare a pointer to 0x1000 and access the registers as pointer, pointer +1, pointer + 2...

A C compiler for embedded applications may have access to registers.

Assembly language supports direct and indirect memory access. Like C you can have general purpose code and point it to different places in memory.

 
A system has a memory map and that gets into hardware. You can always use in line assembly code to access registers. Or you can write assembly code and link it to the C code.

A processor has a memory bus. Imagine a 16 bit bus. Memory locations of devices like a serial or internet chip are decoded by hardware.

For example a chip may have its select pin enabled when 0x1000 appears on the bus. The registers or memory in the chip are then accessed from the physical address on the bus.
R0 0x1000
R1 0x1001

and so on.

In C code I would declare a pointer to 0x1000 and access the registers as pointer, pointer +1, pointer + 2...

A C compiler for embedded applications may have access to registers.

Assembly language supports direct and indirect memory access. Like C you can have general purpose code and point it to different places in memory.

Yeah, and C allows a lot of really powerful cross-compiler options for macroing such hardware accesses, too.

I really like the model of C++ coding that applies extern "C" for headering in C objects so to combine the model of "code that just operates globally available in the environment on  hardware" vs "code that operates specifically on abstract stuff".

I'm pretty big on hybrid programming for various kinds of comm libraries, too, if only because it is really nice to be able to use the same lightweight library for talking to hardware as the hardware uses to talk back.

Then, I'm one of those psychopaths who think that all libraries that fulfill RPC or serial buffer applications should fit entirely in and make sense in C, because if you need more than that, you already overcomplicated and fucked it up.
 
When you pass by value temporary copy of the passed variable is created on the stack. Pass by reference and the function has direct access to the variable.

Call foo and the b variable does not change. Call foo2 and b changes. Therein lies the pointer risk., inadvertent changing of variables used by different functions. Call foo3 and the array a is changed.

All variables are pointers. Something to keep in mind is thatevrything in memory is sequntil and one dimensional. If ints a and b are in memory order a b if f you declare a pointer to a the next increment of the pointer gives you b. Another example of how errant pointers can cause problems. If a and b are in a C++ object and private the only way to access them would be through public functions in the object. The compiler will not allow you to set a pointer to a and b.

int q,w,*p;
p = &w put address of w in p
q = *p put contents located at address p in q


void foo(int *t){
//pass by reference
*t = 1000;
}

void foo2(int t){
//pass by value
t = 1000;
}


void foo3(int x[]){
//pass by reference
x[2] = 3;
}



int main()
{
int b = 1, a[10];

foo2(b);
printf("foo2 b = %d\n",b);
foo(&b);
printf("foo b = %d\n",b)

foo3(a);

}
 
Last edited:
When you pass by value temporary copy of the passed variable is created on the stack. Pass by reference and the function has direct access to the variable.

Call foo and the b variable does not change. Call foo2 and b changes. Therein lies the pointer risk., inadvertent changing of variables used by different functions. Call foo3 and the array a is changed.

All variables are pointers. Something to keep in mind is thatevrything in memory is sequntil and one dimensional. If ints a and b are in memory order a b if f you declare a pointer to a the next increment of the pointer gives you b. Another example of how errant pointers can cause problems. If a and b are in a C++ object and private the only way to access them would be through public functions in the object. The compiler will not allow you to set a pointer to a and b.

int q,w,*p;
p = &w put address of w in p
q = *p put contents located at address p in q


void foo(int *t){
//pass by reference
*t = 1000;
}

void foo2(int t){
//pass by value
t = 1000;
}


void foo3(int x[]){
//pass by reference
x[2] = 3;
}



int main()
{
int b = 1, a[10];

foo2(b);
printf("foo2 b = %d\n",b);
foo(&b);
printf("foo b = %d\n",b)

foo3(a);

}
This is why const is important: if you wish to pass by reference rather than value, because your object or structure is large and complicated, you can mark functions to take and return const pointers to const objects and guarantee that the function catching the pointer to read it can't call anything that modifies the object.

This can lead to some compiler issues wherein it complains when you violate this requirement, but that's the whole point isn't it? To enforce care around modifying variables?

You can even mark whole methods in C++ as const to prevent modification to members from being allowed in the invocation.
 
I am long out of practice just doodling.

A two dimensional array is stored sequentially. Yo can set a pointer to a multidimensional array and index through it.

Both printings give the same result.

You can find the size of an array by sizeof(array)/sizeof(array[0]) but it does not give you rows and columns, just the libear size in memory.

Declarinfunction variable as static means it remains when a function call ends and g0es out of scope. It is useful when you use recursion.

int main()
{
int *p,i = 0, x[2][2];
p = &x[0][0];
x[0][0] = 10; x[0][1] = 20;
x[1][0] = 30; x[1][1] = 40;

for(i = 0;i<2;i++){
printf(" %d \n",x[0]);
printf(" %d \n",x[1]);
}

for(i = 0;i<4;i++){
printf(" %d \n",p);

}




}
 
As to value vs. reference, plain C does all function calls by value. This is OK for input of lightweight data, like integers or floats or characters, but not for heavyweight data or when one wants to return a modified value in an arg.

For that, C++ offers call by reference, with &, thus hiding a pointer. Thus, instead of transmitting a pointer, int *num, one does int &num, and if one has some heavyweight object that one does not want changed, something like const int &num.

There are THREE modes: Call by value, call by name, call by reference. But I always found the distinction between name and reference to be useless and often just an annoyance. If I need a copy of a structure I'll just . . . copy it myself. Maybe my extreme DIY attitude is why I like C and assembly languages best!

But I didn't know C++ had a "int &num; " What is the use anyway? To do this in C you just pass " &num " as an argument, perhaps first doing a copy. What's the advantage of the alleged call-by-reference in C++? Is it really just to hide the copy? (On what grounds? Details confuse the programmer?)

Indeed. Debugging C code can be a lot easier than other languages. More effort at the front end, less effort at back end.

Good point.

A system has a memory map and that gets into hardware. You can always use in line assembly code to access registers. Or you can write assembly code and link it to the C code.

Memory-mapped I/O is ubiquitous. Closely related -- but higher-level -- are viewports where, for example, you write directly to the display pixel array, or a construct that "does the right thing."

Also seen, though much less common, was I/O-mapped memory. (A reversal of the memory-mapped I/O idea.)

In the late 1980's when I was paying some attention, C compilers ranged from the very bad (Green-something?) to the very good (e.g. Sun Micro). With the Sun compiler you can write a C function in assembler, link it in to the C compilation, and, if the function is simple enough, the peephole optimizer will be able to elide all of the linkage instructions. ` bfffo(x) ' for example becomes just the single bfffo instruction.

That was 30+ years ago. I suppose compilers are even much better now.
 
There are THREE modes: Call by value, call by name, call by reference. But I always found the distinction between name and reference to be useless and often just an annoyance. If I need a copy of a structure I'll just . . . copy it myself. Maybe my extreme DIY attitude is why I like C and assembly languages best!

But I didn't know C++ had a "int &num; " What is the use anyway? To do this in C you just pass " &num " as an argument, perhaps first doing a copy. What's the advantage of the alleged call-by-reference in C++? Is it really just to hide the copy? (On what grounds? Details confuse the programmer?)
Call by reference is an alternative to using a pointer. So instead of declaring f(int *numptr), you declare f(int &num). To use it, instead of f(&num), you do f(num).
 
@ Steve — You're right about shortcuts for multiple-dimensionaL arrays, but it's better to declare a pointer (as a working register in typical practice) to a 1-D array for your use; this makes the code much more portable and avoids compiler warnings.

If Arr is conceptually a 2-D array, which will be accessed via " Arr[y][x] " it is still possible to declare Arr in four ways:

int Arr[35][80];
int *(Arr[35]);
int (*Arr)[80];
int **Arr;

It should often be possible to process the Arr with code that will need few if any changes if/when the type definition of Arr changes.

I am long out of practice just doodling.

A two dimensional array is stored sequentially. Yo can set a pointer to a multidimensional array and index through it.

Both printings give the same result.

You can find the size of an array by sizeof(array)/sizeof(array[0]) but it does not give you rows and columns, just the libear size in memory.

Not quite. If array is a 2-D array, then " sizeof(array[0] ) " will be — did you guess? — the size of " array[0] ". Thus the macro will return the number of rows in the table, NOT the number of bytes.
 
There are THREE modes: Call by value, call by name, call by reference. But I always found the distinction between name and reference to be useless and often just an annoyance. If I need a copy of a structure I'll just . . . copy it myself. Maybe my extreme DIY attitude is why I like C and assembly languages best!

But I didn't know C++ had a "int &num; " What is the use anyway? To do this in C you just pass " &num " as an argument, perhaps first doing a copy. What's the advantage of the alleged copy-by-reference in C++? Is it really just to hide the copy? (On what grounds? Details confuse the programmer?)
Call by reference is an alternative to using a pointer. So instead of declaring f(int *numptr), you declare f(int &num). To use it, instead of f(&num), you do f(num).

Are you saying it's equivalent to C's call by name, but just uses different syntax?
 
Are you saying it's equivalent to C's call by name, but just uses different syntax?
Yes. Swammerdami, can you point me to any documentation of the difference in plain C between:
  • Call by value
  • Call by name
  • Call by reference
According to  C (programming language) C does calls by value, and the usual workaround for doing calls by reference is to use pointers. These are then transmitted by value.

Some things may still need pointers, like linked lists. But C++ has a safe alternative to them in its Standard Template Library: smart pointers. There are several kinds, and C++ supports three kinds:
  • unique_ptr - Can be moved to a new owner, but not copied or shared.
  • shared_ptr - Reference-counted smart pointer. Use when you want to assign one raw pointer to multiple owners, for example, when you return a copy of a pointer from a container but want to keep the original.
  • weak_ptr - A weak_ptr provides access to an object that is owned by one or more shared_ptr instances, but does not participate in reference counting. Use when you want to observe an object, but do not require it to remain alive.
 
There are THREE modes: Call by value, call by name, call by reference. But I always found the distinction between name and reference to be useless and often just an annoyance. If I need a copy of a structure I'll just . . . copy it myself. Maybe my extreme DIY attitude is why I like C and assembly languages best!

But I didn't know C++ had a "int &num; " What is the use anyway? To do this in C you just pass " &num " as an argument, perhaps first doing a copy. What's the advantage of the alleged copy-by-reference in C++? Is it really just to hide the copy? (On what grounds? Details confuse the programmer?)
Call by reference is an alternative to using a pointer. So instead of declaring f(int *numptr), you declare f(int &num). To use it, instead of f(&num), you do f(num).

Are you saying it's equivalent to C's call by name, but just uses different syntax?
In my experience calling by reference using int &data allows more succinct understanding of what is going on.

It also allows return by reference as well, which is nice.

I've started moving towards offering arguments as const-by-address, and it saves a lot of headaches in accessing big objects in a less abstract way.

Honestly, while I CAN swing several layers of abstraction at once, I like not needing to.

It also prevents reassigning different addresses to the argument pointers.
 
C++ mostly uses static typing, something that may be annoying when coding, but something that enables catching a lot of bugs at compile time rather than run time.

That static typing is why Microsoft created TypeScript - a sort of type-safe front end for JavaScript.

Static typing is absolutely worth it. For the most part I like typing as strict as possible. The only case that annoys me is C# you can't use an enum as an array index. I loathe JavaScript, I'll have to look into TypeScript.
 
My previous comments about call by {value,reference,name} were confused and confusing. Please ignore them and let me start over.

IMO, a beautiful thing about C is how simple and deterministic it is. Once you learn the simple rules of C, it should be easy to determine, by simple LOCAL inspection, what any small piece of C code does — this is NOT the case with C++ which "hides" relevancies in class definitions. (Pointers, and their distinction from arrays, may be hard to grasp at first. But understanding them is worth the time investment.)

Are you saying it's equivalent to C's call by name, but just uses different syntax?
Yes. Swammerdami, can you point me to any documentation of the difference in plain C between:
  • Call by value
  • Call by name
  • Call by reference

C has exactly ONE call mechanism: Call by value.

In the C function
Code:
      whatever foo (..., some_type george, ...) {
                 ...  george ...
                 ...  george ...
                 ...  george ...
      }
you can change EVERY occurrence of " george " to " *george " (or " (*george) " as needed) and get, in effect, call by reference. But this is NOT a special feature of C; it is not a "cutesy" way to get "call by reference" without introducing a new key-word. It is just a natural and inevitable consequence of the way C's simple unary " * " operator works.

I don't NEED to think about "call by reference" in C. I just need to know C's " * " operator and the useful idea follows AUTOMATICALLY.

How about the weird overloading of C++'s " & " operator? Is there a simple way to transform it into correct C syntax? If so, why bother to have it? If not, what makes it useful? I'll guess the answer to the second question, if any, has something to do with hobbling the programmer's ability to write through pointers. I am happy to code UNHOBBLED, but thanks anyway! :cool:


[off-topic] There is one case where C, despite good intentions, "gets it wrong," sort of:
Code:
              int          *xp = 0;
              int*         zp = 0;
              int          *yp;
              yp    = 0;
xp and yp are handled identically in the above fragment but they APPEAR different (*xp=0 vs yp=0). Some avoid confusion by changing the position of white-space as I've shown with zp. My work-around is just to avoid initializers of that form. They have no purpose except to reduce the line-count slightly.
 
My previous comments about call by {value,reference,name} were confused and confusing. Please ignore them and let me start over.

IMO, a beautiful thing about C is how simple and deterministic it is. Once you learn the simple rules of C, it should be easy to determine, by simple LOCAL inspection, what any small piece of C code does — this is NOT the case with C++ which "hides" relevancies in class definitions. (Pointers, and their distinction from arrays, may be hard to grasp at first. But understanding them is worth the time investment.)

Are you saying it's equivalent to C's call by name, but just uses different syntax?
Yes. Swammerdami, can you point me to any documentation of the difference in plain C between:
  • Call by value
  • Call by name
  • Call by reference

C has exactly ONE call mechanism: Call by value.

In the C function
Code:
      whatever foo (..., some_type george, ...) {
                 ...  george ...
                 ...  george ...
                 ...  george ...
      }
you can change EVERY occurrence of " george " to " *george " (or " (*george) " as needed) and get, in effect, call by reference. But this is NOT a special feature of C; it is not a "cutesy" way to get "call by reference" without introducing a new key-word. It is just a natural and inevitable consequence of the way C's simple unary " * " operator works.

I don't NEED to think about "call by reference" in C. I just need to know C's " * " operator and the useful idea follows AUTOMATICALLY.

How about the weird overloading of C++'s " & " operator? Is there a simple way to transform it into correct C syntax? If so, why bother to have it? If not, what makes it useful? I'll guess the answer to the second question, if any, has something to do with hobbling the programmer's ability to write through pointers. I am happy to code UNHOBBLED, but thanks anyway! :cool:


[off-topic] There is one case where C, despite good intentions, "gets it wrong," sort of:
Code:
              int          *xp = 0;
              int*         zp = 0;
              int          *yp;
              yp    = 0;
xp and yp are handled identically in the above fragment but they APPEAR different (*xp=0 vs yp=0). Some avoid confusion by changing the position of white-space as I've shown with zp. My work-around is just to avoid initializers of that form. They have no purpose except to reduce the line-count slightly.
(Nitpick: assigning a 0 to a pointer may generate warnings due to assigning a literal of integral type to a pointer type. The proper way is to assign nullptr or null depending on version.)
 
In plain C, one makes a null pointer with 0, and the macro NULL usually expands to 0. But recent C++ also supports "nullptr" for null pointers.

A common use case for null pointers is optional values, and that can be handled in C++ with the "pair" template (tuple with two values). But most recently, the "optional" template has been introduced for supporting optional values.

It has member functions has_value(), value() (throws exception for absent value), and value_or() (uses arg value for absent value).

From the looks of it, C++ is going far from its roots as "C with classes".
 
The military calls it mission creep. In engineering it is design creep.

You keep adding until you end up with a complicated mess. Its human nature. We humans get too clever for our own good.

The nest book on software ever written was The C Programming Language by K&R. Like C short and compact.

The idea I had with C was it was bare bones, but you cold build whatever you needed.
 
In plain C, one makes a null pointer with 0, and the macro NULL usually expands to 0. But recent C++ also supports "nullptr" for null pointers.

A common use case for null pointers is optional values, and that can be handled in C++ with the "pair" template (tuple with two values). But most recently, the "optional" template has been introduced for supporting optional values.

It has member functions has_value(), value() (throws exception for absent value), and value_or() (uses arg value for absent value).

From the looks of it, C++ is going far from its roots as "C with classes".
I have no problem stepping further from C, in C++, though, insofar as it adds useful structures and standard practices and models.

In fact, you just brought up a way that significantly streamlines requirement-heavy code for when realtime sequential data transformation is necessary (say, you have a piece of data that can be filtered with a series of linear transforms), you can, instead of having hacky solutions like setting floats to NAN for when any value is invalid, simply use these weak pointer-based abstractions to add a natural flag to the data (flagged by pointer-is-null).

This also opens use of NAN testing for when floating point math actually goes tits or divides by zero or whatever.
 
@ Steve — You're right about shortcuts for multiple-dimensionaL arrays, but it's better to declare a pointer (as a working register in typical practice) to a 1-D array for your use; this makes the code much more portable and avoids compiler warnings.

If Arr is conceptually a 2-D array, which will be accessed via " Arr[y][x] " it is still possible to declare Arr in four ways:

int Arr[35][80];
int *(Arr[35]);
int (*Arr)[80];
int **Arr;

It should often be possible to process the Arr with code that will need few if any changes if/when the type definition of Arr changes.

I am long out of practice just doodling.

A two dimensional array is stored sequentially. Yo can set a pointer to a multidimensional array and index through it.

Both printings give the same result.

You can find the size of an array by sizeof(array)/sizeof(array[0]) but it does not give you rows and columns, just the libear size in memory.

Not quite. If array is a 2-D array, then " sizeof(array[0] ) " will be — did you guess? — the size of " array[0] ". Thus the macro will return the number of rows in the table, NOT the number of bytes.
My point on pointers was that physical memory is linear.

Sizeof(array[0]) gives you the size in bytes of the data type. Sizeof(array) gives the size of the array in bytes. Sizeof(array)/sizeof(array[0]] gives you the number of entries in the array but not how it is structured in terms of rows and columns.

I never found an

The Sclab scropted tool I use has a builtin that returns rows and locums of an array. It is not typed so an array is created by using it.
 
In plain C, one makes a null pointer with 0, and the macro NULL usually expands to 0. But recent C++ also supports "nullptr" for null pointers.

A common use case for null pointers is optional values, and that can be handled in C++ with the "pair" template (tuple with two values). But most recently, the "optional" template has been introduced for supporting optional values.

It has member functions has_value(), value() (throws exception for absent value), and value_or() (uses arg value for absent value).

From the looks of it, C++ is going far from its roots as "C with classes".
I have no problem stepping further from C, in C++, though, insofar as it adds useful structures and standard practices and models.

In fact, you just brought up a way that significantly streamlines requirement-heavy code for when realtime sequential data transformation is necessary (say, you have a piece of data that can be filtered with a series of linear transforms), you can, instead of having hacky solutions like setting floats to NAN for when any value is invalid, simply use these weak pointer-based abstractions to add a natural flag to the data (flagged by pointer-is-null).

This also opens use of NAN testing for when floating point math actually goes tits or divides by zero or whatever.
Or you praoctively test for things like divide by ze before it happens.
 
Back
Top Bottom