C基本原理——在变量和指针上有问题

时间:2021-10-29 22:32:06

I’m having some problems learning C and I really have no where else to turn for advice. I come from a list of OOP languages such as JavaScript and mainly Python, so C is a major change and I’m hitting quite a few bumps trying to learn the fundamentals. I initially started with Zed Shaw’s “Learn C the Hard Way”, but he doesn’t really teach anything in the book. Yeah, he makes you write a lot of code and change stuff up, but I’m not learning why the code works and it is just leading to more confusion as the examples build in complication.

我在学习上遇到了一些问题,我真的没有别的地方可以寻求建议。我来自一个OOP语言列表,比如JavaScript和主要是Python,所以C是一个主要的变化,我正在尝试学习一些基础知识。我最初是从泽德·肖的《学习——艰难之路》开始的,但他在书中并没有教我任何东西。是的,他让你写了很多代码,并改变了一些东西,但是我不知道为什么代码会起作用,而且它会导致更多的混乱,因为这些例子会造成复杂的情况。

The main problems I am having are the difference between variables and pointers (I thought it was pretty distinct until I saw some examples that I’ll be posting below, which completely blurred the line between the two).

我遇到的主要问题是变量和指针之间的差异(我认为它是非常不同的,直到我看到下面的一些例子,它们完全模糊了两者之间的界限)。

For example, I understand that declaring and initializing an int called a and a pointer, p would look like this:

例如,我知道声明和初始化一个名为a和指针的int数,p会是这样的:

int a;
int *p;

a = 12;
p = &a;

What confuses me is when you declare variables that look like pointers, but aren’t really pointers at all (or are they?). For example:

令我困惑的是,当您声明看起来像指针的变量,但实际上根本不是指针(或者它们是指针吗?)例如:

char *string = "This is a string";
printf("%s\n", string);

What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function? There are many examples like this that confuse me.

定义和初始化的字符串是什么?这是一个指针吗?如果是的话,为什么不在printf函数中打印时取消引用呢?有很多这样的例子让我困惑。

Another example I came across that made no sense whatsoever:

我遇到的另一个例子毫无意义:

int i;
scanf("%d", &i);

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value? It gets even more complicated with arrays and structs, which is where I stopped and decided I needed to find some advice.

当&号应该引用变量内存中的位置而不是值时,这个函数如何更新整型i的值?使用数组和结构会变得更加复杂,这就是我停下来的地方,我决定需要找到一些建议。


I honestly feel embarrassed posting such a noob question, but everyone starts somewhere. These fundamentals are something I know I need to be able to understand before moving on, but it is so hard for me to make sense of it when I see examples of code that contradict what I had just learned. I know this is a very general question but I am hoping some of you could either explain these basics or point me in a direction where I can better learn/understand this. Most video tutorials I have come across are too general and same with the text tutorials online, where they tell you how to do something, but don’t explain it, which causes some serious problems down the line.

我真的很不好意思发这样的问题,但是每个人都是从某个地方开始的。这些基本原理是我知道的,在继续之前我需要能够理解的,但是当我看到与我刚学过的代码相矛盾的例子时,我很难理解它们。我知道这是一个非常普遍的问题,但我希望你们中的一些人能够解释这些基本问题,或者为我指明一个方向,让我更好地了解/理解这些问题。我遇到的大多数视频教程都太普通了,和在线的文本教程一样,它们告诉你如何做一些事情,但不要解释,这会导致一些严重的问题。

6 个解决方案

#1


6  

I will try and explain this slightly differently than everyone else.

我将尝试用稍微不同于其他人的方式来解释这一点。

Considering that you are coming from JavaScript and Python I will avoid the term "reference" when talking about pointers. This is because though they are similar they are not the same.

考虑到您来自JavaScript和Python,在谈到指针时,我将避免使用“引用”一词。这是因为它们虽然相似,但并不相同。

What is a pointer?

什么是指针?

A pointer is a variable that stores an address. It's that simple. A pointer to an int stores the address where the int is stored.

指针是存储地址的变量。就是这么简单。指向int的指针存储int所在的地址。

De-reference

De-reference

When you dereference a pointer you are telling the compiler to operate, not on the address, but on what is stored at that address.

当您取消引用指针时,您是在告诉编译器操作,而不是在地址上操作,而是在地址上存储的内容上操作。

int *p;
int a = 7;

p = &a;
*p = 5;

The *p = 5 tells the compiler to go to the address that is stored inside p and store the value 5 there (as an integer since the pointer p points to an integer). So wherever we can use variables, we can use dereferenced pointers to variables in their stead.

*p = 5告诉编译器到存储在p中的地址并将值5存储在那里(作为一个整数,因为指针p指向一个整数)。所以无论我们在哪里使用变量,我们都可以用去引用指针来代替变量。

Should you use:

你应该使用:

int *p;

p = 5;

Then you would be assigning an address (a memory location) of 5 (wherever that may be in memory) to the pointer. If you try and use memory locations that are not allowed your program is likely to crash.

然后,您将为指针指定一个地址(内存位置)为5(可能在内存中)。如果您尝试使用不允许的内存位置,您的程序可能会崩溃。

Address of

地址

The & operator takes the address of some variable. It really doesn't care what it is, it can even take addresses of pointers.

&运算符取某个变量的地址。它真的不在乎它是什么,它甚至可以取指针的地址。

int a;
int *p;
int **pp;

pp = &p;
p = &a;

That is all it does.

这就是它所做的一切。

Your examples

你的例子

String example

字符串的例子

char *string = "This is a string";
printf("%s\n", string);

The reason your are confused is that in JavaScript and Python you can treat a string as if it can fit into a variable. It can't! In C a string is a sequence of characters stored sequentially in memory (an array of characters). That is why we can use pointers. What we do is we make the pointer point to the first character and then we know where the whole string is (since it is sequential). Also, in C we don't store the size of the string. Instead, the string starts at the pointer and ends when we encounter a zero byte '\0' or simply 0.

您感到困惑的原因是,在JavaScript和Python中,您可以将字符串视为可以放入变量。它不能!在C语言中,字符串是存储在内存中的字符序列(字符数组)。这就是为什么我们可以使用指针。我们要做的是让指针指向第一个字符,然后我们知道整个字符串在哪里(因为它是连续的)。同样,在C中,我们不存储字符串的大小。相反,字符串从指针开始,当遇到0字节的'\0'或仅仅是0时结束。

scanf example

scanf例子

int i;
scanf("%d", &i);

The reason scanf is given the address of i is because it will put an integer that is entered through the console into the location where i is. This way scanf can actually have more than one returned value, meaning that:

scanf的地址是我的地址,因为它将把一个整数输入到我所在的位置。这样,scanf实际上可以有多个返回值,这意味着:

int i, j;
scanf("%d%d", &i, &j);

is possible. Since scanf knows the addresses of your variables, it can update them with the correct values.

是可能的。由于scanf知道变量的地址,因此可以用正确的值更新它们。

#2


3  

I'll answer your specific questions about why printf does not get passed a dereferenced pointer, and why scanf gets passed an address, and hopefully that will make some things clearer.

我将回答您的特定问题,关于为什么printf不会通过一个取消引用的指针,以及为什么scanf会被传递一个地址,希望这将使一些事情更清楚。


First of all, a C-style string is, by convention, an array of characters in memory, terminated by a \0 character (called a NUL terminator). The way C-style strings are kept track of is by keeping a pointer to the first character of a string.

首先,按照惯例,c样式的字符串是内存中的字符数组,由\0字符(称为NUL终止符)终止。c样式字符串的跟踪方式是通过一个指向字符串的第一个字符的指针。

When printf("%s\n", some_str) is called, where some_str is of type char*, what printf does is print the character pointed to by some_str, then the character after it (in memory, located by simply incrementing the pointer some_str), then the character after that, until it finds a \0, and then it stops printing.

当printf(" % s \ n " some_str),some_str char *类型的,什么printf由some_str打印字符指出,然后这个角色后(在内存中,通过简单地增加some_str)的指针,然后这个角色之后,直到找到一个\ 0,然后停止印刷。

The same procedure is used by other C-style string manipulating functions, like strcpy, strlen, etc.

其他c风格的字符串处理函数也使用相同的过程,如strcpy、strlen等。

One reason this is done is that strings tend to be of different lengths, thus to require different amounts of memory, but it is inconvenient to have a data type that is of variable size. So, we have a convention for the format of a string in memory (essentially the \0 terminator convention), and we just point to the string with a char*.

这样做的一个原因是字符串往往具有不同的长度,因此需要不同数量的内存,但是使用可变大小的数据类型是不方便的。因此,我们有一个关于内存中字符串格式的约定(本质上是\0终结者约定),我们只需要用一个char*来指向字符串。

If you dereference a char *, you will get a single character, not a string as you may expect, so be careful about this.

如果取消对char *的引用,您将得到一个字符,而不是您可能期望的字符串,因此要小心。


Why does scanf("%d", &i) get passed an address to an int, rather than the int itself?

为什么scanf(“%d”和i)会将地址传递给int,而不是int本身?

The reason is that in C, function arguments get passed by value, meaning that when you pass something to a function, a copy of it is made and handed to the function, and whatever the function does to the copy, the original stays as it was. What are the implications of this?

原因是,在C中,函数参数通过值传递,这意味着当你向一个函数传递一个东西时,它的一个拷贝被创建并传递给这个函数,无论函数对这个拷贝做什么,原始的都保持原样。这意味着什么?

For one thing, if you have a function like:

首先,如果你有一个函数

void add_two(int i) {
  i = i + 2;
}

If you call: int i = 3; add_two(i);, the value of i will not change, because add_two just got a copy of i. On the other hand, if you have a function like:

如果你调用:int i = 3;add_two(i);, i的值不会改变,因为add_two刚刚得到了i的一个拷贝,另一方面,如果你有如下函数:

void really_add_two(int *i) {
  *i = *i + 2;
}

Now int i = 3; really_add_two(&i); will result in i having the value 5. This is because a pointer to i is given to really_add_two, and the function modifies the data at that memory location (via the dereferences).

现在int i = 3;really_add_two(我);会得到值5。这是因为一个指向i的指针被赋予了really_add_two,并且函数在那个内存位置(通过取消引用)修改数据。

The reason scanf needs to be given an address is the same as the above.

需要给scanf一个地址的原因与上面的相同。


If you are serious about learning C, you should pick up Kernighan and Richie, "The C Programming Language." It will be much better than going through tutorials online, and well worth it in the long run.

如果你真的想学习C语言,你应该学习Kernighan和Richie,“C编程语言”。这将比在网上浏览教程要好得多,而且从长远来看是值得的。

#3


2  

a pointer is a variable that holds a memory address instead of the value.

指针是一个包含内存地址而不是值的变量。

when you write

当你写

int a;

you are specifying that a can hold an integer

您正在指定一个可以容纳一个整数。

+-----+
| int | 
+-----+

a is somewhere in the memory and the address of a is written &a

a在记忆中的某个地方,a的地址是写着的和

      +-----+
&a -> | int | 
      +-----+

when you write

当你写

int *p;

you are specifying that p can hold a pointer to an integer i.e. a memory address.

你指定p可以保存一个指向整数的指针,也就是一个内存地址。

     +---------+
p -> |   int   |
     +---------+

for example it can point to a

例如它可以指向a

p = &a;

int i;
scanf( "%d", &i );

in order to understand the above you need to understand how function arguments are passed to a function and what actually functions are.

为了理解上面的内容,您需要了解函数参数是如何传递给函数的,以及函数是什么。

a function is also a pointer so when you call a function you are just telling the compiler that you want to execute code at a certain memory address. a stack is used to pass arguments and results between the function and the caller.

函数也是一个指针,所以当你调用一个函数时,你只是告诉编译器你想要在某个内存地址执行代码。堆栈用于在函数和调用者之间传递参数和结果。

so

所以

int foo(int n) { return 11; }

int j = 10;
int i = foo(j);

tells the compiler that it should execute the assembly instructions at address foo but first push 10 (j) on the stack, a copy of j is pushed on the stack and foo is executed, when foo is about to return it pushes 11 on the stack and returns, the return value 11 is then popped by the caller and assigned to i. notice it is a copy of value 11 that is pushed on the stack.

告诉编译器,它应该执行汇编指令地址foo但首先把10(j)堆栈,j的一个副本被推在堆栈和执行foo,当foo即将返回它推动11在堆栈上并返回,返回值11然后突然由调用者和分配给我注意到它是值的副本11 push到栈上。

if you want the function to change the value of the argument you cannot push a copy of the variable on the stack. instead you need to pass a copy of the memory address of the variable

如果希望函数更改参数的值,则不能将变量的副本推到堆栈上。相反,您需要传递变量的内存地址的副本

int foo(int *p) { *p = 12; return 11; }

int j = 10;
int i = foo(&j);

now j will be 12 and i 11

j = 12 i11

scanf is a bit more complicated than my example, it can take variable number of arguments and uses the first argument ("%d") as a format specifier to determine the data type and size of the following arguments. the %d tells it to expect an int pointer.

scanf比我的示例稍微复杂一点,它可以使用可变数量的参数,并使用第一个参数(“%d”)作为格式说明符来确定以下参数的数据类型和大小。%d告诉它期望有一个int指针。

#4


1  

What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function?

定义和初始化的字符串是什么?这是一个指针吗?如果是的话,为什么不在printf函数中打印时取消引用呢?

It is a pointer. It points directly to the very first character in your string. You don't have to dereference it because printf will accept a char pointer.. and print everything until it finds a null terminator (which is automatically added for you).

这是一个指针。它直接指向字符串中的第一个字符。您不必取消引用,因为printf将接受一个char指针。并打印所有内容,直到找到空终止符(自动添加)。

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value?

当&号应该引用变量内存中的位置而不是值时,这个函数如何更新整型i的值?

Because &i makes the argument be passed in as an int*. So the function would dereference and add.. somewhat like this:

因为&将参数作为int*传入。所以函数会去引用和添加。有些是这样的:

// NOTE: not actual scanf implementation
void scanf(char* format, int * input) {
    *input = get_number(); // store value in variable
}

#5


1  

How about reading some basics about pointers?

阅读一些关于指针的基础知识怎么样?

http://cslibrary.stanford.edu/106/

http://cslibrary.stanford.edu/106/

I will answer your questions below:

我将回答以下问题:

What confuses me is when you declare variables that look like pointers, but aren’t really pointers at all (or are they?). For example:

令我困惑的是,当您声明看起来像指针的变量,但实际上根本不是指针(或者它们是指针吗?)例如:

char *string = “This is a string”;
printf(“%s\n”, string);

Yes they are! Its a char pointer.

是的他们!一个字符指针。

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value?

当&号应该引用变量内存中的位置而不是值时,这个函数如何更新整型i的值?

It depends on the function signature. If you look at what scanf accepts as argument list, it will be clear to you.

它取决于函数签名。如果您查看scanf接受的参数列表,您将清楚地了解它。

#6


1  

    char *string = "This is a string";
    printf("%s\n", string);
What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function? There are many examples like this that confuse me.

First, a little background:

首先,一些背景:

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to intialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

除非是运算符的操作数或一元&操作符,或者是一个字符串文字被用于intialize另一个数组的声明,一个“T n元数组”类型的表达式将转换(“衰变”)一个“T”指针类型的表达式,和表达式的值将数组的第一个元素的地址。

The string literal "This is a string" is an expression of type "17-element array of char" (16 characters plus the 0 terminator). Since it is not the operand of either the sizeof or unary & operators, and since it isn't being used to initialize an array of char, the type of the expression is converted to "pointer to char", and the value of the expression is the address of the first element.

字符串文字“This is a string”是“char的17元素数组”(16个字符加上0终止符)的表达式。由于它不是sizeof或一元&操作符的操作数,而且由于它不用于初始化char数组,表达式的类型被转换为“指向char的指针”,表达式的值是第一个元素的地址。

The variable string is declared as type "pointer to char" (char *), and it is initialized with the address of the string literal.

变量字符串被声明为“指向char的指针”(char *)类型,并用字符串文字的地址初始化。

In the printf call, the conversion specifier %s expects its corresponding argument to have type char *; it will print characters starting at the specified address until it sees a 0 terminator. This is why the variable isn't dereferenced in the printf call.

在printf调用中,转换说明符%s期望其对应的参数具有类型char *;它将打印从指定地址开始的字符,直到看到一个0终止符。这就是为什么在printf调用中变量没有被取消引用。

    int i;
    scanf("%d", &i);
How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value? It gets even more complicated with arrays and structs, which is where I stopped and decided I needed to find some advice.

C passes all function arguments by value, meaning the formal parameter in the function definition and the actual parameter in the function call are two different objects in memory. Updating the formal parameter has no effect on the actual parameter. For example, imagine a simple swap function, like so:

C通过值传递所有函数参数,这意味着函数定义中的形式参数和函数调用中的实际参数是内存中的两个不同对象。更新形式参数对实际参数没有影响。例如,想象一个简单的交换函数,如下所示:

void swap( int a, int b ) { int tmp = a; a = b; b = tmp; return; }
...
int x = 0, y = 1;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( x, y );
printf( " after swap: x = %d, y = %d\n", x, y );

If you compiled and ran this code, you'd see the same values for x and y in both output statements; a and b are different objects in memory from x and y, and changing the values of a and b has no effect on the values of x and y.

如果你编译并运行这段代码,你会在两个输出语句中看到x和y的相同值;a和b是内存中不同于x和y的对象,改变a和b的值对x和y的值没有影响。

Since we want to modify the values of x and y, we must pass pointers to these variables to the function, like so:

由于我们要修改x和y的值,我们必须将这些变量的指针传递给函数,如下所示:

void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; return; }
...
int x = 0, y = 1;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( &x, &y );
printf( " after swap: x = %d, y = %d\n", x, y );

Instead of swapping the values of a and b, we swap the values of what a and b point to; the expressions *a and *b refer to the same objects in memory as x and y.

不是交换a和b的值,而是交换a和b点的值;表达式*a和*b指的是内存中与x和y相同的对象。

This is why you must pass a pointer to i in the scanf call (the scanf conversion specifier %d expects the corresponding argument to have type int *); you would not be able to update the value of i otherwise.

这就是为什么必须在scanf调用中传递一个指向i的指针(scanf转换说明符%d期望相应的参数具有int *类型);否则,您将无法更新i的值。

I haven't been too impressed with "Learn C The Hard Way"; then again, almost all C references have some fatal flaws. Kernighan and Ritchie's The C Programming Language, while pretty long in the tooth (it doesn't cover anything introduced in the last two standard revisions), is still probably the best overall introduction to the language. I've also heard good things about King's C Programming: A Modern Approach. My go-to desktop reference is Harbison & Steele's C: A Reference Manual, although it's exactly what it says it is - a reference manual, and as such not that big on explaining basic concepts.

我对“学习——艰难之路”印象不深;同样,几乎所有C引用都有一些致命的缺陷。Kernighan和Ritchie的C语言编程语言,虽然很长一段时间内(它没有涵盖上两个标准版本中引入的任何内容),但仍然可能是对该语言最好的全面介绍。我也听说过King's C编程的好东西:一种现代的方法。我的首选桌面参考是Harbison & Steele的《C:参考手册》,尽管它确实是这么说的——一本参考手册,因此不太擅长解释基本概念。

#1


6  

I will try and explain this slightly differently than everyone else.

我将尝试用稍微不同于其他人的方式来解释这一点。

Considering that you are coming from JavaScript and Python I will avoid the term "reference" when talking about pointers. This is because though they are similar they are not the same.

考虑到您来自JavaScript和Python,在谈到指针时,我将避免使用“引用”一词。这是因为它们虽然相似,但并不相同。

What is a pointer?

什么是指针?

A pointer is a variable that stores an address. It's that simple. A pointer to an int stores the address where the int is stored.

指针是存储地址的变量。就是这么简单。指向int的指针存储int所在的地址。

De-reference

De-reference

When you dereference a pointer you are telling the compiler to operate, not on the address, but on what is stored at that address.

当您取消引用指针时,您是在告诉编译器操作,而不是在地址上操作,而是在地址上存储的内容上操作。

int *p;
int a = 7;

p = &a;
*p = 5;

The *p = 5 tells the compiler to go to the address that is stored inside p and store the value 5 there (as an integer since the pointer p points to an integer). So wherever we can use variables, we can use dereferenced pointers to variables in their stead.

*p = 5告诉编译器到存储在p中的地址并将值5存储在那里(作为一个整数,因为指针p指向一个整数)。所以无论我们在哪里使用变量,我们都可以用去引用指针来代替变量。

Should you use:

你应该使用:

int *p;

p = 5;

Then you would be assigning an address (a memory location) of 5 (wherever that may be in memory) to the pointer. If you try and use memory locations that are not allowed your program is likely to crash.

然后,您将为指针指定一个地址(内存位置)为5(可能在内存中)。如果您尝试使用不允许的内存位置,您的程序可能会崩溃。

Address of

地址

The & operator takes the address of some variable. It really doesn't care what it is, it can even take addresses of pointers.

&运算符取某个变量的地址。它真的不在乎它是什么,它甚至可以取指针的地址。

int a;
int *p;
int **pp;

pp = &p;
p = &a;

That is all it does.

这就是它所做的一切。

Your examples

你的例子

String example

字符串的例子

char *string = "This is a string";
printf("%s\n", string);

The reason your are confused is that in JavaScript and Python you can treat a string as if it can fit into a variable. It can't! In C a string is a sequence of characters stored sequentially in memory (an array of characters). That is why we can use pointers. What we do is we make the pointer point to the first character and then we know where the whole string is (since it is sequential). Also, in C we don't store the size of the string. Instead, the string starts at the pointer and ends when we encounter a zero byte '\0' or simply 0.

您感到困惑的原因是,在JavaScript和Python中,您可以将字符串视为可以放入变量。它不能!在C语言中,字符串是存储在内存中的字符序列(字符数组)。这就是为什么我们可以使用指针。我们要做的是让指针指向第一个字符,然后我们知道整个字符串在哪里(因为它是连续的)。同样,在C中,我们不存储字符串的大小。相反,字符串从指针开始,当遇到0字节的'\0'或仅仅是0时结束。

scanf example

scanf例子

int i;
scanf("%d", &i);

The reason scanf is given the address of i is because it will put an integer that is entered through the console into the location where i is. This way scanf can actually have more than one returned value, meaning that:

scanf的地址是我的地址,因为它将把一个整数输入到我所在的位置。这样,scanf实际上可以有多个返回值,这意味着:

int i, j;
scanf("%d%d", &i, &j);

is possible. Since scanf knows the addresses of your variables, it can update them with the correct values.

是可能的。由于scanf知道变量的地址,因此可以用正确的值更新它们。

#2


3  

I'll answer your specific questions about why printf does not get passed a dereferenced pointer, and why scanf gets passed an address, and hopefully that will make some things clearer.

我将回答您的特定问题,关于为什么printf不会通过一个取消引用的指针,以及为什么scanf会被传递一个地址,希望这将使一些事情更清楚。


First of all, a C-style string is, by convention, an array of characters in memory, terminated by a \0 character (called a NUL terminator). The way C-style strings are kept track of is by keeping a pointer to the first character of a string.

首先,按照惯例,c样式的字符串是内存中的字符数组,由\0字符(称为NUL终止符)终止。c样式字符串的跟踪方式是通过一个指向字符串的第一个字符的指针。

When printf("%s\n", some_str) is called, where some_str is of type char*, what printf does is print the character pointed to by some_str, then the character after it (in memory, located by simply incrementing the pointer some_str), then the character after that, until it finds a \0, and then it stops printing.

当printf(" % s \ n " some_str),some_str char *类型的,什么printf由some_str打印字符指出,然后这个角色后(在内存中,通过简单地增加some_str)的指针,然后这个角色之后,直到找到一个\ 0,然后停止印刷。

The same procedure is used by other C-style string manipulating functions, like strcpy, strlen, etc.

其他c风格的字符串处理函数也使用相同的过程,如strcpy、strlen等。

One reason this is done is that strings tend to be of different lengths, thus to require different amounts of memory, but it is inconvenient to have a data type that is of variable size. So, we have a convention for the format of a string in memory (essentially the \0 terminator convention), and we just point to the string with a char*.

这样做的一个原因是字符串往往具有不同的长度,因此需要不同数量的内存,但是使用可变大小的数据类型是不方便的。因此,我们有一个关于内存中字符串格式的约定(本质上是\0终结者约定),我们只需要用一个char*来指向字符串。

If you dereference a char *, you will get a single character, not a string as you may expect, so be careful about this.

如果取消对char *的引用,您将得到一个字符,而不是您可能期望的字符串,因此要小心。


Why does scanf("%d", &i) get passed an address to an int, rather than the int itself?

为什么scanf(“%d”和i)会将地址传递给int,而不是int本身?

The reason is that in C, function arguments get passed by value, meaning that when you pass something to a function, a copy of it is made and handed to the function, and whatever the function does to the copy, the original stays as it was. What are the implications of this?

原因是,在C中,函数参数通过值传递,这意味着当你向一个函数传递一个东西时,它的一个拷贝被创建并传递给这个函数,无论函数对这个拷贝做什么,原始的都保持原样。这意味着什么?

For one thing, if you have a function like:

首先,如果你有一个函数

void add_two(int i) {
  i = i + 2;
}

If you call: int i = 3; add_two(i);, the value of i will not change, because add_two just got a copy of i. On the other hand, if you have a function like:

如果你调用:int i = 3;add_two(i);, i的值不会改变,因为add_two刚刚得到了i的一个拷贝,另一方面,如果你有如下函数:

void really_add_two(int *i) {
  *i = *i + 2;
}

Now int i = 3; really_add_two(&i); will result in i having the value 5. This is because a pointer to i is given to really_add_two, and the function modifies the data at that memory location (via the dereferences).

现在int i = 3;really_add_two(我);会得到值5。这是因为一个指向i的指针被赋予了really_add_two,并且函数在那个内存位置(通过取消引用)修改数据。

The reason scanf needs to be given an address is the same as the above.

需要给scanf一个地址的原因与上面的相同。


If you are serious about learning C, you should pick up Kernighan and Richie, "The C Programming Language." It will be much better than going through tutorials online, and well worth it in the long run.

如果你真的想学习C语言,你应该学习Kernighan和Richie,“C编程语言”。这将比在网上浏览教程要好得多,而且从长远来看是值得的。

#3


2  

a pointer is a variable that holds a memory address instead of the value.

指针是一个包含内存地址而不是值的变量。

when you write

当你写

int a;

you are specifying that a can hold an integer

您正在指定一个可以容纳一个整数。

+-----+
| int | 
+-----+

a is somewhere in the memory and the address of a is written &a

a在记忆中的某个地方,a的地址是写着的和

      +-----+
&a -> | int | 
      +-----+

when you write

当你写

int *p;

you are specifying that p can hold a pointer to an integer i.e. a memory address.

你指定p可以保存一个指向整数的指针,也就是一个内存地址。

     +---------+
p -> |   int   |
     +---------+

for example it can point to a

例如它可以指向a

p = &a;

int i;
scanf( "%d", &i );

in order to understand the above you need to understand how function arguments are passed to a function and what actually functions are.

为了理解上面的内容,您需要了解函数参数是如何传递给函数的,以及函数是什么。

a function is also a pointer so when you call a function you are just telling the compiler that you want to execute code at a certain memory address. a stack is used to pass arguments and results between the function and the caller.

函数也是一个指针,所以当你调用一个函数时,你只是告诉编译器你想要在某个内存地址执行代码。堆栈用于在函数和调用者之间传递参数和结果。

so

所以

int foo(int n) { return 11; }

int j = 10;
int i = foo(j);

tells the compiler that it should execute the assembly instructions at address foo but first push 10 (j) on the stack, a copy of j is pushed on the stack and foo is executed, when foo is about to return it pushes 11 on the stack and returns, the return value 11 is then popped by the caller and assigned to i. notice it is a copy of value 11 that is pushed on the stack.

告诉编译器,它应该执行汇编指令地址foo但首先把10(j)堆栈,j的一个副本被推在堆栈和执行foo,当foo即将返回它推动11在堆栈上并返回,返回值11然后突然由调用者和分配给我注意到它是值的副本11 push到栈上。

if you want the function to change the value of the argument you cannot push a copy of the variable on the stack. instead you need to pass a copy of the memory address of the variable

如果希望函数更改参数的值,则不能将变量的副本推到堆栈上。相反,您需要传递变量的内存地址的副本

int foo(int *p) { *p = 12; return 11; }

int j = 10;
int i = foo(&j);

now j will be 12 and i 11

j = 12 i11

scanf is a bit more complicated than my example, it can take variable number of arguments and uses the first argument ("%d") as a format specifier to determine the data type and size of the following arguments. the %d tells it to expect an int pointer.

scanf比我的示例稍微复杂一点,它可以使用可变数量的参数,并使用第一个参数(“%d”)作为格式说明符来确定以下参数的数据类型和大小。%d告诉它期望有一个int指针。

#4


1  

What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function?

定义和初始化的字符串是什么?这是一个指针吗?如果是的话,为什么不在printf函数中打印时取消引用呢?

It is a pointer. It points directly to the very first character in your string. You don't have to dereference it because printf will accept a char pointer.. and print everything until it finds a null terminator (which is automatically added for you).

这是一个指针。它直接指向字符串中的第一个字符。您不必取消引用,因为printf将接受一个char指针。并打印所有内容,直到找到空终止符(自动添加)。

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value?

当&号应该引用变量内存中的位置而不是值时,这个函数如何更新整型i的值?

Because &i makes the argument be passed in as an int*. So the function would dereference and add.. somewhat like this:

因为&将参数作为int*传入。所以函数会去引用和添加。有些是这样的:

// NOTE: not actual scanf implementation
void scanf(char* format, int * input) {
    *input = get_number(); // store value in variable
}

#5


1  

How about reading some basics about pointers?

阅读一些关于指针的基础知识怎么样?

http://cslibrary.stanford.edu/106/

http://cslibrary.stanford.edu/106/

I will answer your questions below:

我将回答以下问题:

What confuses me is when you declare variables that look like pointers, but aren’t really pointers at all (or are they?). For example:

令我困惑的是,当您声明看起来像指针的变量,但实际上根本不是指针(或者它们是指针吗?)例如:

char *string = “This is a string”;
printf(“%s\n”, string);

Yes they are! Its a char pointer.

是的他们!一个字符指针。

How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value?

当&号应该引用变量内存中的位置而不是值时,这个函数如何更新整型i的值?

It depends on the function signature. If you look at what scanf accepts as argument list, it will be clear to you.

它取决于函数签名。如果您查看scanf接受的参数列表,您将清楚地了解它。

#6


1  

    char *string = "This is a string";
    printf("%s\n", string);
What is string when it is defined and initialized? Is it a pointer? And if it is, why don’t you dereference it when printing it in the printf function? There are many examples like this that confuse me.

First, a little background:

首先,一些背景:

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to intialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

除非是运算符的操作数或一元&操作符,或者是一个字符串文字被用于intialize另一个数组的声明,一个“T n元数组”类型的表达式将转换(“衰变”)一个“T”指针类型的表达式,和表达式的值将数组的第一个元素的地址。

The string literal "This is a string" is an expression of type "17-element array of char" (16 characters plus the 0 terminator). Since it is not the operand of either the sizeof or unary & operators, and since it isn't being used to initialize an array of char, the type of the expression is converted to "pointer to char", and the value of the expression is the address of the first element.

字符串文字“This is a string”是“char的17元素数组”(16个字符加上0终止符)的表达式。由于它不是sizeof或一元&操作符的操作数,而且由于它不用于初始化char数组,表达式的类型被转换为“指向char的指针”,表达式的值是第一个元素的地址。

The variable string is declared as type "pointer to char" (char *), and it is initialized with the address of the string literal.

变量字符串被声明为“指向char的指针”(char *)类型,并用字符串文字的地址初始化。

In the printf call, the conversion specifier %s expects its corresponding argument to have type char *; it will print characters starting at the specified address until it sees a 0 terminator. This is why the variable isn't dereferenced in the printf call.

在printf调用中,转换说明符%s期望其对应的参数具有类型char *;它将打印从指定地址开始的字符,直到看到一个0终止符。这就是为什么在printf调用中变量没有被取消引用。

    int i;
    scanf("%d", &i);
How does this function update the value of integer i, when the ampersand is supposed to reference the location in memory of the variable, not the value? It gets even more complicated with arrays and structs, which is where I stopped and decided I needed to find some advice.

C passes all function arguments by value, meaning the formal parameter in the function definition and the actual parameter in the function call are two different objects in memory. Updating the formal parameter has no effect on the actual parameter. For example, imagine a simple swap function, like so:

C通过值传递所有函数参数,这意味着函数定义中的形式参数和函数调用中的实际参数是内存中的两个不同对象。更新形式参数对实际参数没有影响。例如,想象一个简单的交换函数,如下所示:

void swap( int a, int b ) { int tmp = a; a = b; b = tmp; return; }
...
int x = 0, y = 1;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( x, y );
printf( " after swap: x = %d, y = %d\n", x, y );

If you compiled and ran this code, you'd see the same values for x and y in both output statements; a and b are different objects in memory from x and y, and changing the values of a and b has no effect on the values of x and y.

如果你编译并运行这段代码,你会在两个输出语句中看到x和y的相同值;a和b是内存中不同于x和y的对象,改变a和b的值对x和y的值没有影响。

Since we want to modify the values of x and y, we must pass pointers to these variables to the function, like so:

由于我们要修改x和y的值,我们必须将这些变量的指针传递给函数,如下所示:

void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; return; }
...
int x = 0, y = 1;
printf( "before swap: x = %d, y = %d\n", x, y );
swap( &x, &y );
printf( " after swap: x = %d, y = %d\n", x, y );

Instead of swapping the values of a and b, we swap the values of what a and b point to; the expressions *a and *b refer to the same objects in memory as x and y.

不是交换a和b的值,而是交换a和b点的值;表达式*a和*b指的是内存中与x和y相同的对象。

This is why you must pass a pointer to i in the scanf call (the scanf conversion specifier %d expects the corresponding argument to have type int *); you would not be able to update the value of i otherwise.

这就是为什么必须在scanf调用中传递一个指向i的指针(scanf转换说明符%d期望相应的参数具有int *类型);否则,您将无法更新i的值。

I haven't been too impressed with "Learn C The Hard Way"; then again, almost all C references have some fatal flaws. Kernighan and Ritchie's The C Programming Language, while pretty long in the tooth (it doesn't cover anything introduced in the last two standard revisions), is still probably the best overall introduction to the language. I've also heard good things about King's C Programming: A Modern Approach. My go-to desktop reference is Harbison & Steele's C: A Reference Manual, although it's exactly what it says it is - a reference manual, and as such not that big on explaining basic concepts.

我对“学习——艰难之路”印象不深;同样,几乎所有C引用都有一些致命的缺陷。Kernighan和Ritchie的C语言编程语言,虽然很长一段时间内(它没有涵盖上两个标准版本中引入的任何内容),但仍然可能是对该语言最好的全面介绍。我也听说过King's C编程的好东西:一种现代的方法。我的首选桌面参考是Harbison & Steele的《C:参考手册》,尽管它确实是这么说的——一本参考手册,因此不太擅长解释基本概念。