C指针问题

C指针问题,c,C,我真的对指针的工作原理感到困惑。我正试图写一些简短的小程序来说明它们是如何工作的,我遇到了一些麻烦。例如: char c[3]; //Creates an array of 3 bytes - the first 2 bytes can be used for characters and the 3rd would need to be used for the terminating zero *c = 'a'; //sets c[0] to 'a' *c++; //moves the p

我真的对指针的工作原理感到困惑。我正试图写一些简短的小程序来说明它们是如何工作的,我遇到了一些麻烦。例如:

char c[3]; //Creates an array of 3 bytes - the first 2 bytes can be used for characters and the 3rd would need to be used for the terminating zero

*c = 'a'; //sets c[0] to 'a'
*c++; //moves the pointer to c[1]
*c = 'b'; //sets c[1] to 'b'
*c++; //moves the pointer to c[2]
*c = '\0' //sets c[2] to the terminating zero
显然,这段代码是不正确的,否则我就不会在论坛上投票:)


我只是在从一本书中理解这一点时遇到了一些困难,有人能简单地解释一下这个概念吗?

c
不是指针,而是数组。虽然在大多数数组上下文中,数组名称会衰减为指针,但不能将数组名称视为可修改的指针。衰变的结果只是暂时的(技术上是右值)

因此,不能将
++
应用于数组的名称。如果要递增指针,请使用指针:

char *d = c;
试一试


*运算符正在尝试取消对指针的引用。你需要做的就是移动指针,然后进行赋值。

首先,
c
这里不是指针,而是数组。数组在某些上下文中可以像指针一样使用,但它们不是一回事。特别是,您可以使用
*c
(就好像它是一个指针一样)来访问第一个位置的值,但是由于
c
实际上不是一个指针,因此您不能通过使用
c++
来更改
c
点的位置

其次,你误解了
*
的意思。在使用指针时,它不仅仅是一种装饰。作为操作员,它意味着“取消引用”,即允许我访问所指向的内容。因此,当您操作指针本身(例如,通过递增指针)而不操作指向的数据时,不需要使用指针

以下是您可能想要的:

char c[3]; // Creates an array of 3 bytes - the first 2 bytes can be used for characters 
           // and the 3rd would need to be used for the terminating zero
char* p_c; // Creates a character pointer that we will use to refer into the c array

p_c = &c[0]; // Assign the address of the first element of the c array to the p_c pointer.
             // This could also be "p_c = c", taking advantage of the fact that in this
             // is one of the circumstances in which an array can be treated as if it were 
             // a pointer to its first element

*p_c = 'a'; //sets c[0] to 'a'
p_c++;      //moves the pointer to c[1] (note no *)
*p_c = 'b'; //sets c[1] to 'b'
p_c++;      //moves the pointer to c[2] (note no *)
*p_c = '\0' //sets c[2] to the terminating zero

指针和数组在C语言中是不同的。混淆的根源是数组被转换成指针(标准名称为decay)。它被称为“衰减”,因为它丢失了一些关于数组类型(即数组大小)的信息

让我们看看

void f( char* p );

char c_array [3];       // define an array
char *c_ptr = c_array;  // define a pointer and set it to point at the beginning of the array
                        // here array "decays" to pointer

*c_ptr = '1';
assert(c_array[0] == '1');
assert(c_ptr[0] == '1');   // this works too... in fact, operator [] is defined
                           // for pointers, not arrays, so in the line above array
                           // decays to pointer too.

++c_ptr;                   // move the pointer
//++c_array;               // -- this won't compile, you can't move the array

*c_ptr++ = '2';
*c_ptr   = '\0';
assert(c_array[1] == '2');
assert(c_array[2] == 0);

assert(sizeof(c_array) == 3);  // no decay here!

assert(sizeof(c_ptr) == sizeof(void*));  // a pointer is just a pointer

f(c_array);                // array-to-pointer decay, again


// now, what happens here?
void g( char param [100] )
{
    ++param;  // it works!
              // you can't pass an array as a parameter by value.
              // The size in the parameter declaration is ignored; it's just a comment.
              // param is a pointer.

    assert(sizeof(param) == sizeof(void*));
              // yes, it's just a pointer

    assert(*param == '2'); // in the call below
}


g(c_array);   // array-to-pointer decay, again
希望这有点帮助

(请注意,为了便于说明,我将声明和语句混合在一起。为了使它成为一个有效的C程序,您必须重新安排一些内容)


编辑:增加了示例的大小

在调试器中单步执行程序并检查所有内容的值有助于我理解指针。在你的白板上画很多图片来巩固你的理解。对我来说,真正巩固这一点的是学习组装并从头开始制作MIPS

尝试在调试器中单步执行此操作,并在白板上绘制一些图表以跟踪执行情况

#include <stdio.h>

int main()
{
    char c_arr[3] = {'a', 'b', '\0'};       // Array of 3 chars.
    char* c_ptr = c_arr; // Now c_ptr contains the address of c_arr.

    // What does it mean that c_ptr "contains the address of c_arr"?
    // Underneath all this talk of "pointers" and "arrays", it's all
    // just numbers stored in memory or registers. So right now, c_ptr is
    // just a number stored somewhere in your computer.

    printf("%p\n", c_ptr);
    // I got `0xbf94393d`. You'll get something different each time you run it.

    // That number (0xbf94393d) is a particular memory location. If you
    // want to use the contents of that memory location, you use the *
    // operator.
    char ch = *c_ptr;
    // Now ch holds the contents of whatever was in memory location 0xbf94393d.
    // You can print it.

    printf("%c\n", ch);
    // You should see `a`.

    // Let's say you want to work with the next memory location. Since
    // the pointer is just a number, you can increment it with the ++ operator.
    c_ptr++;
    // Let's print it to see what it contains.

    printf("%p\n", c_ptr);
    // I got 0xbf94393e. No surprises here, it's just a number -- the
    // next memory location after what was printed above.

    // Again, if we want to work with the value we can use the *
    // operator. You can put this on the left side of an assignment
    // to modify the memory location.
    *c_ptr = 'z';

    // Since c_ptr was pointing to the middle of our array when we
    // performed that assignment, we can inspect the array to see
    // the change.
    printf("%c\n", c_arr[1]);

    // Again, c_ptr is just a number, so we can point it back to
    // where it was. You could use -- for this, but I'll show -=.
    c_ptr -= 1;

    // We can also move by more than one. This will make the pointer
    // contain the address of the last memory location in the array.
    c_ptr = c_ptr + 2;

    return 0;
}
#包括
int main()
{
char c_arr[3]={'a','b','\0'};//由3个字符组成的数组。
char*c_ptr=c_arr;//现在c_ptr包含c_arr的地址。
//c_ptr“包含c_arr的地址”是什么意思?
//在所有这些关于“指针”和“数组”的讨论之下,都是
//只是存储在内存或寄存器中的数字。所以现在,c_ptr是
//只是一个储存在你电脑里的数字。
printf(“%p\n”,c\u ptr);
//我得到了“0xbf94393d”。每次运行它都会得到不同的结果。
//该数字(0xbf94393d)是一个特定的内存位置
//要使用该内存位置的内容,请使用*
//接线员。
char ch=*c_ptr;
//现在ch保存内存位置0xbf94393d中任何内容的内容。
//你可以把它打印出来。
printf(“%c\n”,ch);
//你应该看到a。
//假设您想使用下一个内存位置。因为
//指针只是一个数字,您可以使用++运算符递增它。
c_ptr++;
//让我们把它打印出来看看里面有什么。
printf(“%p\n”,c\u ptr);
//我得到了0xbf94393e。没什么奇怪的,这只是一个数字
//上面打印的内容之后的下一个内存位置。
//同样,如果我们想使用这个值,我们可以使用*
//接线员。你可以把这个放在作业的左边
//修改内存位置。
*c_ptr='z';
//因为当我们
//执行该任务后,我们可以检查阵列以查看
//改变。
printf(“%c\n”,c_arr[1]);
//同样,c_ptr只是一个数字,所以我们可以将它指向
//它在哪里。你可以用-,但我会显示-=。
c_ptr-=1;
//我们也可以移动多个指针。这将使指针
//包含数组中最后一个内存位置的地址。
c_ptr=c_ptr+2;
返回0;
}
这是我尝试拍的一张照片。这个盒子是你电脑的内存。内存中的每个位置都分配了一个号码,我们称这个号码为地址

++++++++++++++++++++++++++++++++++++++++++++
|  NAME   |   ADDRESS    |   VALUE         |
+=========+==============+=================+
|  c_arr  |  0xbf94393d  |   'a'           |
|         |  0xbf94393e  |   'b'           |
|         |  0xbf94393f  |   '\0'          |
+---------+--------------+-----------------+
|  c_ptr  +  <someaddr>  |   0xbf94393d    |
+------------------------------------------+
++++++++++++++++++++++++++++++++++++++++++++
|名称|地址|值|
+=========+==============+=================+
|c|u arr | 0xbf94393d |“a”|
|| 0xbf94393e |“b”|
|| 0xbf94393f |'\0'|
+---------+--------------+-----------------+
|c|ptr+| 0xbf94393d|
+------------------------------------------+

当您访问时,比如说,
c_arr[0]
,您正在使用表中的第一行。请注意,
c_ptr
的值是表中最上面一行的地址。当您说
*c_ptr
时,您是在告诉CPU使用
0xbf94393d
作为要操作的地址。所以
*c_ptr='z'
有点像说“嘿,去0xbf94393d,在那里留下一个'z'”——在这条街上,地址真的很大。

数组的名称可以被视为指向它的第一个元素的指针,尽管它是一个常量指针,因此不能指向任何其他位置。所以
c++
是不允许的。

如果这是从一本书上抄来的,我想你需要另一本书+1致查尔斯·贝利-c不是指针。这不是一本书——这是一些东西
++++++++++++++++++++++++++++++++++++++++++++
|  NAME   |   ADDRESS    |   VALUE         |
+=========+==============+=================+
|  c_arr  |  0xbf94393d  |   'a'           |
|         |  0xbf94393e  |   'b'           |
|         |  0xbf94393f  |   '\0'          |
+---------+--------------+-----------------+
|  c_ptr  +  <someaddr>  |   0xbf94393d    |
+------------------------------------------+