(C beginner alert)
(C初学者警报)
I want to read in some integers from the user and store them in an array. So:
我想从用户读取一些整数并将它们存储在一个数组中。所以:
int main (void)
{
int i, num, cont = 0;
int arre[10];
for (int i=0;i<5;i++)
{
scanf("%d", arre[i]);
etc.
When I run this, I get a Segmentation Fault 11 on OSX. If I run it with Valgrind, the problem occurs when I enter the first integer, and it tells me:
当我运行它时,我在OSX上得到了分段故障11。如果我用Valgrind运行它,当我输入第一个整数时会出现问题,它告诉我:
==1610== Command: ./ArraysAndPointers
==1610==
2
==1610== Use of uninitialised value of size 8
==1610== at 0x18F0BA: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==1610== by 0x18718A: scanf (in /usr/lib/system/libsystem_c.dylib)
==1610== by 0x100000F2D: main (ArraysAndPointers.c:11)
==1610==
==1610== Invalid write of size 4
==1610== at 0x18F0BA: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==1610== by 0x18718A: scanf (in /usr/lib/system/libsystem_c.dylib)
==1610== by 0x100000F2D: main (ArraysAndPointers.c:11)
==1610== Address 0x0 is not stack'd, malloc'd or (recently) free'd
If I add an & in front of arre[i], if fixes the problem. But I don't know why. I'm struggling with the fact that I am reading in an integer, but storing (apparently) its memory address in the array. Yet when I check its value as it appears in the resultant array, it's the int itself, and not the memory address. Why is this so?
如果我在arre [i]前添加&,如果解决了问题。但我不知道为什么。我正在努力解决这个问题,即我正在读取整数,但是(显然)将其内存地址存储在数组中。然而,当我检查它在结果数组中出现的值时,它本身就是int,而不是内存地址。为什么会这样?
Note: I am fundamentally struggling to grasp pointers/memory addresses and their relation to arrays, char* etc. (see my other questions) and despite haven undertaken several C training modules with different providers and watched various explanations online, I've yet to encounter someone who can definitively nail the concept for me. Particularly, I'm interested to know when and why pointers are needed. If anyone can suggest a good reference/video/tutorial/article for me, I would be very grateful.
注意:我基本上都在努力掌握指针/内存地址及其与数组,char *等的关系(参见我的其他问题),尽管我们已经与不同的提供商进行了多次C培训模块并在线观看了各种解释,但我还没有遇到一个可以为我明确指出这个概念的人。特别是,我很想知道何时以及为什么需要指针。如果有人能为我推荐一个好的参考/视频/教程/文章,我将非常感激。
5 个解决方案
#1
Let's replace i
with 0 here for the sake of explanation.
为了便于解释,让我们用0替换i。
scanf("%d", arre[0]);
This code goes to the array, looks up the first element, and finds that it's 17 (or whatever), so it passes 17 as the second argument to scanf()
. But scanf()
is expecting a pointer, so it gets horribly confused when it sees a 17 and ends up crashing your application.
此代码转到数组,查找第一个元素,并发现它是17(或其他),因此它将17作为第二个参数传递给scanf()。但是scanf()期待一个指针,所以当它看到17并且最终崩溃你的应用程序时会非常混乱。
scanf("%d", &arre[0]);
This code calculates the location of the first element in the array and passes that pointer to scanf()
. scanf()
happily writes a value to the memory addressed by that pointer.
此代码计算数组中第一个元素的位置,并将该指针传递给scanf()。 scanf()愉快地将值写入该指针寻址的内存中。
#2
scanf()
with %d
format specifier expects int *
as its second parameter.
具有%d格式说明符的scanf()期望int *作为其第二个参数。
So going by the standards you need to provide the address of the variable in which you are going to store the scanned value.
因此,按照标准,您需要提供要存储扫描值的变量的地址。
Man Says:
d
Matches an optionally signed decimal integer; the next pointer must be a pointer to int.
匹配可选的带符号十进制整数;下一个指针必须是指向int的指针。
Answering your extended question on pointers:
回答关于指针的扩展问题:
Any good C book would explain in detail why pointers are needed. Simply putting it you need some memory to store your scanned value. In this case it is the array and you need to scan the value to array by providing the address where you need to store the value which is given by &arr[i]
任何好的C书都会详细解释为什么需要指针。简单地说,你需要一些内存来存储你的扫描值。在这种情况下,它是数组,您需要通过提供需要存储由&arr [i]给出的值的地址来将值扫描到数组
#3
scanf() method wants 2 parameters:
scanf()方法需要2个参数:
- format of expected inputs
- location where to store the scanned values
预期投入的格式
存储扫描值的位置
C language can only pass parameters to function by value. You can not tell a function to store a value in a parameter and that the parameter will keep this value (technically, parameters are copied on the stack
- a LIFO queue - and remove from the stack when the function ends). So if you store a value in a parameter (an int for example), the value will be lost at the end of the function.
C语言只能通过值传递参数。您无法告诉函数将值存储在参数中,并且参数将保留此值(从技术上讲,参数将复制到堆栈上 - LIFO队列 - 并在函数结束时从堆栈中删除)。因此,如果将值存储在参数(例如int)中,则该值将在函数末尾丢失。
If you want to keep the value, as you can't pass in the variable itself, you pass in the memory address (that is &var) of the variable in main memory (the heap
) and not the stack. It will be received by the function in a parameter of type (int*), which means what is designed by this memory address is an int variable
.
如果要保留该值,因为您无法传递变量本身,则传入主内存(堆)中的变量的内存地址(即&var),而不是堆栈。它将由函数在类型(int *)的参数中接收,这意味着该内存地址设计的是一个int变量。
So with this address (passed on the stack), you can modify what is in main memory (the heap), and the value written at this address in the heap will be kept even after the end of the function, because emptying the stack will not empty the heap.
因此,使用此地址(在堆栈上传递),您可以修改主内存(堆)中的内容,并且即使在函数结束后,堆中此地址处写入的值也将保留,因为清空堆栈将没有清空堆。
You store the value in a variable at address a (such as int *a : a is a pointer to an int) by writing: *a = <my int>
.
您可以通过编写以下内容将值存储在地址a的变量中(例如int * a:a是指向int的指针):* a =
And for information, C array variable is in fact a pointer to the first element of the array (the address of the first element): arre is the same value as &arre[0]
有关信息,C数组变量实际上是指向数组第一个元素的指针(第一个元素的地址):arre与&arre [0]的值相同
And arre[n]
is *(arre + n)
: what is stored at (the address of the array plus an offset of n elements size).
并且arre [n]是*(arre + n):存储在(数组的地址加上n个元素大小的偏移量)的内容。
#4
C passes all function arguments by value, meaning the formal parameter in the function definition is a different object in memory from the actual parameter in the function call. Look at the following example:
C按值传递所有函数参数,这意味着函数定义中的形式参数是内存中与函数调用中的实际参数不同的对象。请看以下示例:
void swap( int a, int b ) { int tmp = a; a = b; b = tmp; }
void foo( void )
{
int x = 1, y = 2;
swap( x, y );
printf( "x = %d, y = %d\n", x, y );
}
The formal parameter a
in swap
is a different object in memory than x
in foo
, so any change we make to a
doesn't affect x
; after the call to swap
, the values of x
and y
remain unchanged.
swap中的形式参数a是内存中与foo中的x不同的对象,因此我们对a的任何更改都不会影响x;在调用swap之后,x和y的值保持不变。
In order for the swap
function to change the values of x
and y
, we must pass pointers to those variables:
为了让swap函数改变x和y的值,我们必须将指针传递给这些变量:
void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; }
void foo( void )
{
int x = 1, y = 2;
swap( &x, &y );
printf( "x = %d, y = %d\n", x, y );
}
This time, instead of passing the values of x
and y
to swap
, we pass the addresses of x
and y
. The variables a
and b
are pointers to x
and y
respectively, so that writing to the expression *a
is the same as writing to x
(similarly, writing to the expression *b
is the same as writing to y
).
这次,我们传递x和y的地址,而不是将x和y的值传递给swap。变量a和b分别是指向x和y的指针,因此写入表达式* a与写入x相同(类似地,写入表达式* b与写入y相同)。
A shorthand way of describing it is
描述它的简便方法是
a == &x --> *a == x
b == &y --> *b == y
When you call scanf
with the argument arre[i]
, you're passing the value of that array element to the function. Unfortunately, scanf
wants the address of that element so that it can write a new value to it. scanf
tried to interpret the value you sent it as the address of an object in memory, hence the segfault.
当您使用参数arre [i]调用scanf时,您将该数组元素的值传递给该函数。不幸的是,scanf想要该元素的地址,以便它可以为它写一个新值。 scanf试图解释你发送它作为内存中对象地址的值,因此是段错误。
This is why you need to use the &
operator on the arre[i]
expression when you pass it as an argument to scanf
.
这就是当你将它作为参数传递给scanf时需要在arre [i]表达式上使用&运算符的原因。
Note that you don't need to use the &
operator if the argument is already a pointer type. Be aware that when you read strings with the %s
conversion specifier, you will usually be passing an array argument, like so:
请注意,如果参数已经是指针类型,则不需要使用&运算符。请注意,当您使用%s转换说明符读取字符串时,通常会传递一个数组参数,如下所示:
char input[81];
scanf( "%s", input );
In this case, the input
argument is implicitly converted from an expression of type "array of char
" to an expression of type "pointer to char
", and the value of the expression is the address of the first element in the array. Under most circumstances, an array expression will "decay" to a pointer expression. As a beginner, this will bite you several times.
在这种情况下,输入参数从“array of char”类型的表达式隐式转换为“指向char的指针”类型的表达式,表达式的值是数组中第一个元素的地址。在大多数情况下,数组表达式将“衰减”为指针表达式。作为一个初学者,这会咬你好几次。
#5
The statement scanf("%d", arre[i]);
will bit-cast arre[i] into an address. This address is not allocated, so it is a so-called "wild pointer". Accessing address that does not belong to you is undefined behavior. If it is a system-protected address, the system will kill the process and give a segmentation fault.
语句scanf(“%d”,arre [i]);会将[i]钻进一个地址。该地址未分配,因此它是所谓的“野指针”。访问不属于您的地址是未定义的行为。如果它是受系统保护的地址,系统将终止进程并发出分段错误。
I'm interested to know when and why pointers are needed.
我很想知道何时以及为什么需要指针。
C functions are always called by value, which means that the parameters in the calling function is just a copy of the arguments in the caller. Modifying the parameters will not affect the arguments. A common example is
C函数总是按值调用,这意味着调用函数中的参数只是调用者中参数的副本。修改参数不会影响参数。一个常见的例子是
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
int main(void)
{
int x = 1, y = 10;
swap(x, y);
printf("x = %d, y = %d\n", x, y); // still origin value
return 0;
}
One reason to use pointers is when you do need to modify the arguments. You can declare (or define) your function like
使用指针的一个原因是你需要修改参数。您可以声明(或定义)您的函数
void swap(int* a, int *b);
and call like
并打电话给
swap(&x, &y);
Another case to use pointers is when the parameter is a big struct, copy of which takes too much time and space. Then its pointer is used. Sometimes a const
qualifier is used to protect it from being modified (but not guaranteed).
使用指针的另一种情况是当参数是一个大结构时,其副本需要太多的时间和空间。然后使用它的指针。有时使用const限定符来保护它不被修改(但不能保证)。
Compare the following foo
and goo
.
比较以下foo和goo。
struct Big { char dummy[1024]};
void foo(struct Big b);
void goo(const struct Big* b);
#1
Let's replace i
with 0 here for the sake of explanation.
为了便于解释,让我们用0替换i。
scanf("%d", arre[0]);
This code goes to the array, looks up the first element, and finds that it's 17 (or whatever), so it passes 17 as the second argument to scanf()
. But scanf()
is expecting a pointer, so it gets horribly confused when it sees a 17 and ends up crashing your application.
此代码转到数组,查找第一个元素,并发现它是17(或其他),因此它将17作为第二个参数传递给scanf()。但是scanf()期待一个指针,所以当它看到17并且最终崩溃你的应用程序时会非常混乱。
scanf("%d", &arre[0]);
This code calculates the location of the first element in the array and passes that pointer to scanf()
. scanf()
happily writes a value to the memory addressed by that pointer.
此代码计算数组中第一个元素的位置,并将该指针传递给scanf()。 scanf()愉快地将值写入该指针寻址的内存中。
#2
scanf()
with %d
format specifier expects int *
as its second parameter.
具有%d格式说明符的scanf()期望int *作为其第二个参数。
So going by the standards you need to provide the address of the variable in which you are going to store the scanned value.
因此,按照标准,您需要提供要存储扫描值的变量的地址。
Man Says:
d
Matches an optionally signed decimal integer; the next pointer must be a pointer to int.
匹配可选的带符号十进制整数;下一个指针必须是指向int的指针。
Answering your extended question on pointers:
回答关于指针的扩展问题:
Any good C book would explain in detail why pointers are needed. Simply putting it you need some memory to store your scanned value. In this case it is the array and you need to scan the value to array by providing the address where you need to store the value which is given by &arr[i]
任何好的C书都会详细解释为什么需要指针。简单地说,你需要一些内存来存储你的扫描值。在这种情况下,它是数组,您需要通过提供需要存储由&arr [i]给出的值的地址来将值扫描到数组
#3
scanf() method wants 2 parameters:
scanf()方法需要2个参数:
- format of expected inputs
- location where to store the scanned values
预期投入的格式
存储扫描值的位置
C language can only pass parameters to function by value. You can not tell a function to store a value in a parameter and that the parameter will keep this value (technically, parameters are copied on the stack
- a LIFO queue - and remove from the stack when the function ends). So if you store a value in a parameter (an int for example), the value will be lost at the end of the function.
C语言只能通过值传递参数。您无法告诉函数将值存储在参数中,并且参数将保留此值(从技术上讲,参数将复制到堆栈上 - LIFO队列 - 并在函数结束时从堆栈中删除)。因此,如果将值存储在参数(例如int)中,则该值将在函数末尾丢失。
If you want to keep the value, as you can't pass in the variable itself, you pass in the memory address (that is &var) of the variable in main memory (the heap
) and not the stack. It will be received by the function in a parameter of type (int*), which means what is designed by this memory address is an int variable
.
如果要保留该值,因为您无法传递变量本身,则传入主内存(堆)中的变量的内存地址(即&var),而不是堆栈。它将由函数在类型(int *)的参数中接收,这意味着该内存地址设计的是一个int变量。
So with this address (passed on the stack), you can modify what is in main memory (the heap), and the value written at this address in the heap will be kept even after the end of the function, because emptying the stack will not empty the heap.
因此,使用此地址(在堆栈上传递),您可以修改主内存(堆)中的内容,并且即使在函数结束后,堆中此地址处写入的值也将保留,因为清空堆栈将没有清空堆。
You store the value in a variable at address a (such as int *a : a is a pointer to an int) by writing: *a = <my int>
.
您可以通过编写以下内容将值存储在地址a的变量中(例如int * a:a是指向int的指针):* a =
And for information, C array variable is in fact a pointer to the first element of the array (the address of the first element): arre is the same value as &arre[0]
有关信息,C数组变量实际上是指向数组第一个元素的指针(第一个元素的地址):arre与&arre [0]的值相同
And arre[n]
is *(arre + n)
: what is stored at (the address of the array plus an offset of n elements size).
并且arre [n]是*(arre + n):存储在(数组的地址加上n个元素大小的偏移量)的内容。
#4
C passes all function arguments by value, meaning the formal parameter in the function definition is a different object in memory from the actual parameter in the function call. Look at the following example:
C按值传递所有函数参数,这意味着函数定义中的形式参数是内存中与函数调用中的实际参数不同的对象。请看以下示例:
void swap( int a, int b ) { int tmp = a; a = b; b = tmp; }
void foo( void )
{
int x = 1, y = 2;
swap( x, y );
printf( "x = %d, y = %d\n", x, y );
}
The formal parameter a
in swap
is a different object in memory than x
in foo
, so any change we make to a
doesn't affect x
; after the call to swap
, the values of x
and y
remain unchanged.
swap中的形式参数a是内存中与foo中的x不同的对象,因此我们对a的任何更改都不会影响x;在调用swap之后,x和y的值保持不变。
In order for the swap
function to change the values of x
and y
, we must pass pointers to those variables:
为了让swap函数改变x和y的值,我们必须将指针传递给这些变量:
void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; }
void foo( void )
{
int x = 1, y = 2;
swap( &x, &y );
printf( "x = %d, y = %d\n", x, y );
}
This time, instead of passing the values of x
and y
to swap
, we pass the addresses of x
and y
. The variables a
and b
are pointers to x
and y
respectively, so that writing to the expression *a
is the same as writing to x
(similarly, writing to the expression *b
is the same as writing to y
).
这次,我们传递x和y的地址,而不是将x和y的值传递给swap。变量a和b分别是指向x和y的指针,因此写入表达式* a与写入x相同(类似地,写入表达式* b与写入y相同)。
A shorthand way of describing it is
描述它的简便方法是
a == &x --> *a == x
b == &y --> *b == y
When you call scanf
with the argument arre[i]
, you're passing the value of that array element to the function. Unfortunately, scanf
wants the address of that element so that it can write a new value to it. scanf
tried to interpret the value you sent it as the address of an object in memory, hence the segfault.
当您使用参数arre [i]调用scanf时,您将该数组元素的值传递给该函数。不幸的是,scanf想要该元素的地址,以便它可以为它写一个新值。 scanf试图解释你发送它作为内存中对象地址的值,因此是段错误。
This is why you need to use the &
operator on the arre[i]
expression when you pass it as an argument to scanf
.
这就是当你将它作为参数传递给scanf时需要在arre [i]表达式上使用&运算符的原因。
Note that you don't need to use the &
operator if the argument is already a pointer type. Be aware that when you read strings with the %s
conversion specifier, you will usually be passing an array argument, like so:
请注意,如果参数已经是指针类型,则不需要使用&运算符。请注意,当您使用%s转换说明符读取字符串时,通常会传递一个数组参数,如下所示:
char input[81];
scanf( "%s", input );
In this case, the input
argument is implicitly converted from an expression of type "array of char
" to an expression of type "pointer to char
", and the value of the expression is the address of the first element in the array. Under most circumstances, an array expression will "decay" to a pointer expression. As a beginner, this will bite you several times.
在这种情况下,输入参数从“array of char”类型的表达式隐式转换为“指向char的指针”类型的表达式,表达式的值是数组中第一个元素的地址。在大多数情况下,数组表达式将“衰减”为指针表达式。作为一个初学者,这会咬你好几次。
#5
The statement scanf("%d", arre[i]);
will bit-cast arre[i] into an address. This address is not allocated, so it is a so-called "wild pointer". Accessing address that does not belong to you is undefined behavior. If it is a system-protected address, the system will kill the process and give a segmentation fault.
语句scanf(“%d”,arre [i]);会将[i]钻进一个地址。该地址未分配,因此它是所谓的“野指针”。访问不属于您的地址是未定义的行为。如果它是受系统保护的地址,系统将终止进程并发出分段错误。
I'm interested to know when and why pointers are needed.
我很想知道何时以及为什么需要指针。
C functions are always called by value, which means that the parameters in the calling function is just a copy of the arguments in the caller. Modifying the parameters will not affect the arguments. A common example is
C函数总是按值调用,这意味着调用函数中的参数只是调用者中参数的副本。修改参数不会影响参数。一个常见的例子是
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
int main(void)
{
int x = 1, y = 10;
swap(x, y);
printf("x = %d, y = %d\n", x, y); // still origin value
return 0;
}
One reason to use pointers is when you do need to modify the arguments. You can declare (or define) your function like
使用指针的一个原因是你需要修改参数。您可以声明(或定义)您的函数
void swap(int* a, int *b);
and call like
并打电话给
swap(&x, &y);
Another case to use pointers is when the parameter is a big struct, copy of which takes too much time and space. Then its pointer is used. Sometimes a const
qualifier is used to protect it from being modified (but not guaranteed).
使用指针的另一种情况是当参数是一个大结构时,其副本需要太多的时间和空间。然后使用它的指针。有时使用const限定符来保护它不被修改(但不能保证)。
Compare the following foo
and goo
.
比较以下foo和goo。
struct Big { char dummy[1024]};
void foo(struct Big b);
void goo(const struct Big* b);