How does this function work without using any pointers? [duplicate] - c

This question already has answers here:
What is array to pointer decay?
(11 answers)
In C, are arrays pointers or used as pointers?
(6 answers)
Closed 4 years ago.
I'm attempting to learn C and going through the K&R book. Many examples online seem to use pointers to return a value from a function. I would think then that the same would be used for this K & R function:
/*
Reverse a string in place
*/
void reverse(char s[])
{
int c, i, j;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--)
{
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
int main()
{
char s[] = "HELLO";
reverse(s);
printf("%s", s);
return (0);
}
I would think that the string would NOT be reversed in this situation. Yet it prints the char array backwards as originally intended by the author.
How does it do that? I don't completely understand pointers yet but I was thinking it would be like reverse(&s) and then void reverse(char *s[]) {...}

Arrays, when passed as arguments to functions, decay to a pointer to their first element. Hence when you pass an any type of array (including a string) into a function, you are effectively passing it by reference and any modifications made to the array within the function will be reflected in the calling code as well, after the function call.
Examine the output of this code to enrich your understanding of pointers and arrays:
#include <stdio.h>
#include <stdlib.h>
void foo(int *arg, size_t len)
{
size_t i;
printf("sizeof arg is %zu\n", sizeof arg);
for(i = 0; i < len; i++)
{
printf("arg[%zu] = %d\n", i, arg[i]);
}
printf("arg's address is %p\n", (void *) arg);
}
int main()
{
int array[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
printf("sizeof array is %zu\n", sizeof array);
printf("array begins at %p in memory\n", (void *) array);
foo(array, 10);
return 0;
}

Since no copy of the string is made anywhere the original string must be modified by the array assignments in reverse. Changing void reverse(char s[]) to void reverse(char *s) would change nothing. An array of unknown size behaves just like a pointer to the array's first element.

I also learned C from K&R, it's one of the best books out there, although after reading it, get a book that covers C99 and C11.
If you look at section 5.3 in the book
5.3 Pointers and Arrays
They write:
When an array name is passed to a function, what is passed is the
location of the initial element. Within the called function, this
argument is a local variable, and so an array name parameter is a
pointer, that is, a variable containing an address.
So although arrays and pointers are different, when you pass an array to a function it does not pass the array, but a pointer to the first element.
some differences:
There is one difference between an array name and a pointer that must
be kept in mind. A pointer is a variable, so pa=a and pa++ are legal.
But an array name is not a variable; constructions like a=pa and a++
are illegal.
One thing to keep in mind about K&R book when you read it. They mention something once, and 50 pages later they use it, unless you remember it, it will look like it came out of nowhere. They don't repeat themselves much in the book.

The array will implicitly be converted to a pointer to the first element of the array when passed as an argument to a function.
That is why the the elements of the original array are modified and not a copy of it since no copy of the array is made; only the address of the first element.

Related

How to know how many elements there are in a pointer array? [duplicate]

This question already has answers here:
In C how to get number of items in array of pointers to char strings
(5 answers)
Closed 4 years ago.
void print(char *arch[], int num){
int i;
for(i=0; i<8; i++)
printf("%s\n", *(arch+i));
}
In this case I knew that arch was formed by 8 elements, but if I didn't know how can I know it? Is there a way?
Since arrays decay to pointers to their first element when passed as arguments to a function, length information is lost as well.
For example:
void bar()
{
int data[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("%zu\n", sizeof data); // output: 10 * sizeof(int)
foo(data);
}
void foo(int *arg)
{
printf("%zu\n", sizeof arg) // output: sizeof(int *)
}
This is why strings are null-terminated in C: so that when pointers to strings (or arrays decayed to pointers) are passed to string-handling functions, their length can still be determined by incrementing to the null-pointer and keeping count.
There is no way of knowing the length of an array given only a pointer to the array without one of:
A "length" parameter
A sentinel value indicating the end of the array (such as the null-terminator for strings)
There is no way you can know the size of array passed to function.
As array decays to pointer to first element.
Compiler will treat.
void print(char *arch[], int num)
as
void print(char **arch, int num)
Thus when you do
sizeof(arch); //It is size of pointer.
Solution:
You can pass size of array additional to function as parameter.
Have known value at the end of the array and loop until you find the expected value.

Char array sizeof() method [duplicate]

This question already has answers here:
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 5 years ago.
Complete example:
#include <stdio.h>
void test(int arr[]) {
int arrSize = (int)(sizeof(arr) / sizeof(arr[0]));
printf("%d\n", arrSize); // 2 (wrong?!)
}
int main (int argc, const char * argv[]) {
int point[3] = {50, 30, 12};
int arrSize = (int)(sizeof(point) / sizeof(point[0]));
printf("%d\n", arrSize); // 3 (correct :-) )
test(point);
return 0;
}
Before passing it to a function, sizeof gives me the correct value. Doing the exact same thing on the exact same array in the function gives weird results. There's one element missing. Why?
When you pass an array into a function in C, the array decays into a pointer to its first element. When you use sizeof on the parameter, you are taking the size of the pointer, not the array itself.
If you need the function to know the size of the array, you should pass it as a separate parameter:
void test(int arr[], size_t elems) {
/* ... */
}
int main(int argc, const char * argv[]) {
int point[3] = {50, 30, 12};
/* ... */
test(point, sizeof(point)/sizeof(point[0]));
/* ... */
}
Also note that, for a similar reason (taking the sizeof a pointer), the sizeof(point)/sizeof(point[0]) trick doesn't work for a dynamically allocated array, only an array allocated on the stack.
Because array is decayed to a pointer when passed as function argument, so sizeof gives you 4 and 8 for 32- and 64-bit platforms respectively.
Also, it's important to understand that sizeof is evaluated at compile time. Since that's the case, it doesn't make sense to expect different output in test() depending on what was passed in. The sizeof calculation was done when the function was compiled.
Because, when it's passed, only the pointer to array is actually being passed.
Your question is also answered at The C Programming FAQ. Question 6.21.
Because in C, C++, and Objective-C, functions cannot actually have array parameters. They only can have parameters that look like array parameters, but they aren't. In your example,
void test(int arr[])
the compiler sees "there is a parameter that looks like an array of int", and it replaces that parameter with a "pointer to int". So the function that you wrote is absolutely, one hundred percent, identical to
void test (int* arr)
Therefore, inside the function sizeof (arr) will give you the size of a "pointer to int".
Because sizeof() does NOT tell you the size of an array in C. It does something completely different.
sizeof C++ reference

Why does this C code sample use a pointer to a pointer?

The following code sample demonstrates the similarities between pointers and arrays. In the second method, the author declares cur_name as a pointer to a pointer. My question is, why is this necessary? Why does he declare a new pointer instead of just using the original pointer, names? I should note, the code compiles and runs fine when I get rid of cur_name and use names, so is it a matter of style rather than function? Any explanation would be appreciated.
#include <stdio.h>
int main(int argc, char *argv[])
{
// create two arrays we care about
int ages[] = {23, 43, 12, 89, 2};
char *names[] = {"Alan", "Frank", "Mary", "John", "Lisa"};
// safely get the size of ages
int count = sizeof(ages) / sizeof(int);
int i = 0;
// first way using indexing
for(i = 0; i < count; i++) {
printf("%s has %d years alive.\n",
names[i], ages[i]);
}
printf("---\n");
// setup the pointers to the start of the arrays
int *cur_age = ages;
char **cur_name = names;
// second way using pointers
for(i = 0; i < count; i++) {
printf("%s is %d years old.\n",
*(cur_name+i), *(cur_age+i));
}
printf("---\n");
// third way, pointers are just arrays
for(i = 0; i < count; i++) {
printf("%s is %d years old again.\n",
cur_name[i], cur_age[i]);
}
printf("---\n");
// fourth way with pointers in a stupid complex way
for(cur_name = names, cur_age = ages;
(cur_age - ages) < count;
cur_name++, cur_age++)
{
printf("%s lived %d years so far.\n",
*cur_name, *cur_age);
}
return 0;
}
The code uses a pointer to a pointer to represent a pointer into an array of C strings. Namely, cur_name is a pointer into the array of C strings called names. Since a string in C is itself represented by a pointer to char, a pointer into the array of such pointers becomes a pointer to a pointer.
Why does he declare a new pointer instead of just using the original pointer, names?
Because names is not a pointer, it is an array of pointers (see the square brackets after the declaration? That is what makes names an array. The single asterisk in front is related to the type of the array element, which is a char* in this program).
Making an array adds a level of indirection: to point into an array of ints you need int*, but to point into an array of int* you need an int** pointer.
If you have an array of type T as below
T a[N];
then if you want to define a pointer to its first element you have to write
T *p = a;
Now substitute T for the type of elements of array names defined as
char *names[] = {"Alan", "Frank", "Mary", "John", "Lisa"};
You will get
typedef char * T;
T names[] = {"Alan", "Frank", "Mary", "John", "Lisa"};
so the pointer to the first element of the array will look like
T *cur_name = names;
If you now will make the reverse substitution you will get
char * *cur_name = names;
^^^^^^
T
The author does increment them in the fourth method, so he needs the temporaries for that, but you are correct about it not being necessary in the second method. Perhaps (s)he is using the assignment to show their equivalence?
I think the examples would be clearer if each method was implemented in its own function.

C modifying array unexpected behaviour

I understand it is not possible to pass an array to a function in C and modify it without using sending a reference to that array, so how is the chess method initialising the array, and it is being printed correctly in main?
int chess(int rows, int cols, int array[rows][cols])
{
/* Go through the rows and colums in the array */
for (int i = 0; i < rows;i++)
{
for (int j = 0; j < cols; j++)
{
/* If the location is even, then print a 0, else print a 1. */
if (((i + j) % 2) ==0)
{
array[i][j] = 0;
}
else
{
array[i][j] = 1;
}
}
}
/* return */
return 0;
}
int main(void)
{
int arrayDimensions;
int noOfTests = 7;
/* run the program for the given amount of tests */
/*for (arrayDimensions = 0; arrayDimensions <= noOfTests; arrayDimensions++)*/
{
/* Declare an array for each dimension */
int array[6][5];
/* call the chess method passing it the arguments specified. */
chess(6, 5, array);
/* Print out the array according to the size of the array. */
for (int i = 0; i < 6; i++)
{
printf("\n");
for (int j = 0; j < 5; j++)
{
printf("%d", array[i][j]);
}
}
/* Create a new line after each row */
printf("\n");
}
}
Though you probably already know most of this, the latter part is relevant, so stay with this for a moment.
What you probably know
C is a pass-by-value language. This means when you do this:
void foo(int x)
{
x = 5;
}
called as
int n = 1;
foo(n);
printf("%d\n", n); // n is still 1
the caller passes a value and the parameter x receives it. But changing x has no effect on the caller. If you want to modify a caller's data variable, you must do so by-address. You do this by declaring the formal parameter to be a pointer-to-type, dereference the pointer to modify the pointed-to data, and finally, pass the address of the variable to modify:
void foo(int *p)
{
*p = 5;
}
called as:
int n = 1;
foo(&n);
printf("%d\n", n); // n is now 5
Why do you care?
So what does any of this have to do with your array? Arrays in C are a contiguous sequence of data of the underlying type of the array, and an array's expression value is the address of its first element.
Chew on that last sentence for a minute. That means the same way an int variable has a value of the int stored within, an array variables has the address of its first element as its "value". By now you know that pointers hold addresses. Knowing that and the previous description means this:
int ar[10];
int *p = ar;
is legal. The "value" of ar is its first-element address, and we're assigning that address to p, a pointer.
Ok then, So the language specifically defined arrays as parameters as simply pointers to their first elements. Therefore both of these are equivalent:
void foo(int *p)
{
*p = 5;
}
void bar(int ar[])
{
ar[0] = 5;
}
And the caller side:
int ar[10];
foo(ar); // legal, ar's "value" is its first element address
bar(ar); // legal, identical to the above.
A phrase I often use when explaining the fundamentals of pointers and arrays is simply this:
A pointer is a variable that holds an address; an array is a variable that is an address.
So whats with this whacky syntax?
int chess(int rows, int cols, int array[rows][cols])
Ah. There is something interesting. Your compiler supports VLA s (variable length arrays). When compiling the code for this function the compiler generates the proper logic to perform the proper access to the passed array. I.e. it knows that this:
array[1][0]
is cols many int values past the beginning of the array. Without the feature of VLAs (and some C compilers don't have them), you would have to do this row-by-column math yourself by hand. Not impossible, but tedious none-the-lesss. And i only briefly mention that C++ doesn't support VLA's; one of the few features C has that C++ does not.
Summary
Arrays are passed by address because when it comes to their "value", that is all they really are: an address.
Note: I worked very hard to avoid using the word "decay" or the phrase "decays to a pointer" in this description precisely because that verb implies some mystical functional operation when in-fact none exists. Arrays don't "decay" to anything. They simply are; Their "value", per the C standard, is the address of their first element. Period. What you do with that address is another matter. And as an address, a pointer can hold their "value" and dereference to access said-same (such as a function parameter).
Personal: I once asked on this forum how long that term (decay) has been buzzed about in C-engineer vernacular, since in the 600+ pages of the C standard it appears exactly ZERO times. The farthest back anyone found was 1988 in the annals of some conspicuous online journal. I'm always curious to note who started it an where, and said-quest continues to elude me.
Anyway, I hope this helps, even a little.
when dealing with arrays you are dealing with an address so if you pass an array into a function and you changed the array in the function, the real array will change.
In other words, if you know pointers an array is a pointer.
I didn't read the code but you have a clear misunderstanding of passing an array to a function.
An array reference IS an address (it's a misnomer to say it's a pointer, but it will behave as such). It works because the function is declared to accept a type of int[][], which allows the function to interact with the array reference as if you'd passed a pointer to the function.
From a reference manual:
When a function parameter is declared as an array, the compiler treats
the declaration as a pointer to the first element of the array. For
example, if x is a parameter and is intended to represent an array of
integers, it can be declared as any one of the following declarations:
int x[]; int *x; int x[10];
So you are passing a reference. The compiler turns your declaration into a pointer reference with also some sizing constraints.

Why is the sizeof function returning to different values for the same array? [duplicate]

This question already has answers here:
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 9 years ago.
Consider this piece of code (or just copy/paste and run it):
#include <stdio.h>
int array[] = {1, 2, 3, 4, 5, 6, 7};
int my_put(int *array)
{
printf("Size of array %lu\n", sizeof(array));
return 0;
}
int main(int argc, char **argv)
{
printf("Size of array %lu\n", sizeof(array));
my_put(array);
return 0;
}
My question is: How come that the sizeof function returns two different values? Why is the size 28 in the main function and 8 in the my_put function?
Because in my_put(), array is an int * pointer, not an array. Your parameter shadows the global array variable. It might be clearer if you rewrite my_put to look like this:
int my_put(int *a)
{
printf("Size of array %zu\n", sizeof array);
printf("Size of a %zu\n", sizeof a);
return 0;
}
This new function doesn't confuse two variables named array. I also fixed it to use the z format specifier for size_t types.
Plenty more at the comp.lang.c FAQ, section 6, Arrays and Pointers, particularly Question 6.21 for your case.
In my_put name array is shadowed by your func's arg and its size is 8 (since it is a pointer). In main you get size of your 'real' array. That's all
In function my_put , array is pointer and its size is of the size of int *. Read C-FAQ 6.21.
Because its not the same "array". You have two arrays here -- the global variable "array" and the argument "array" to my_put. Now the second one is actually a pointer that points at the first one, but its a distinct variable with a different type (int pointer vs int array), so it has a different size.

Resources