学习c无法理解指针

时间:2018-02-05 23:11:00

标签: c

void
bar (char *arg, char *targ, int len)
{
  int i;
  for (i = 0; i < len; i++)
  {
    *targ++ = *arg++;
  }
}

现在学习c,朋友给我发了这个片段,我无法理解它的作用。对指针部分的解释会很有帮助。根据我的理解,它似乎是将arg的值复制到tar中为我的字符?

3 个答案:

答案 0 :(得分:4)

指针是存储地址的变量。该地址可以是地址 另一个变量:

int a = 18;
int *pa = &a;

或者它可以是动态分配的内存块的开始:

int *p = malloc(sizeof *p);

重要的是指针允许您访问后面的值 地址。您可以使用* - 运算符:

取消引用指针
int a = 18;
int *pa = &a;

*pa = 10;

printf("a=%d\n", a); // will print 10

对于这些例子,这可能看起来不是什么大问题,但它是, 因为您可以将指针传递给函数,然后这些函数可以进行交互 指针指向的内存,取决于内存块,甚至 修改它。

指针也可以指向对象序列的开始,例如指向 数组的开头:

int arr[] = { 1, 3, 5 };
int *p = arr;

注意p[0]为1,p[1]为3,p[2]为5.也可以更改 做p[1] = -14;的价值观。这也是解除引用,但你也可以使用 * - 操作者:

p[1] = 12;
// is equivalent to
*(p + 1) = 12;

这就是你的代码片段所使用的内容。忘了一秒循环。看一看 在这一行:

*targ++ = *arg++;

这可以改写为:

targ[0] = arg[0];
targ = targ + 1;  // or targ = &(targ[1])
arg = arg + 1;    // or arg = &(arg[1])

现在它更清楚它在做什么。它复制第一个字符的值 arg指向targ指向的位置。在那之后 argtarg会递增以前进到下一个元素 序列。 1

所以循环正在做的是复制len指向的arg个对象 targ。这可以用于将字符串复制到另一个char数组中。但它是 不安全,因为不清楚'\0' - 终止字节是否被复制 目前尚不清楚缓冲区是否足够大(意味着大于 len)。如果它们不是字符串而是字节序列,那么这个函数会 没关系。

在C中,字符串只是以'\0' - 终止字节结尾的字符序列。 因此,它们使用char数组存储并传递给函数 作为char的指针,指向字符串的开头。我们可以改写 这个功能更加安全,如下所示:

int safe_copy_string(char *dest, char *source, size_t dest_size)
{
    if(dest == NULL || source == NULL)
        return 0;

    if(dest_size == 0)
        return 1; // no space to copy anything

    // copying one element less than dest_size
    // that last element if dest should be \0
    for(size_t i = 0; i < dest_size - 1; ++i)
    {
        *dest++ = *source++;

        if(*(source - 1) == '\0')
            break; // copied sources's 0-terminating byte
    }

    dest[dest_size - 1] = 0; // making sure it is 0-terminated

    return 1;
}

<强> Footenotes

1 这里值得一提的是++ - 运算符。这是后增量 在指针的情况下,用于向操作数(对于整数)添加1的运算符 将指针前进1,使其指向下一个对象。

当你这样做时:

int a = 6;
int b = a++;
// a == 7, b == 6

a初始化为6.初始化b时,编译器将使用。{ 初始化的当前值a,但后增量 运算符的副作用是它会将a的值增加1 这恰好是由sequence points的规则定义的。什么 重要的是,在b的初始化中,使用了a的当前值 并且在作业a之后将有一个新值。

答案 1 :(得分:0)

指针指向数据,通常包含数据的内存地址。指针是正常的&#39; c&#39;变量。

运营商&#39; *&#39;当使用指针时,应用时,告诉编译器在指针表示的位置访问数据。

运算符&#39; ++&#39;,应用于指针,增加其值,使其指向与前一个数据元素相邻的下一个数据元素。所以,对于&#39; char *&#39;指针,它通过&#39; 1&#39;增加地址。指向字符串中的下一个字符。

在您的情况下,$BaseVM表示:访问指针引用的数据&#39; targ&#39;然后递增指针的值。

*targ++

在上面的表达式中,程序采用了指向&#39; arg&#39;并将其分配给&#39; targ&#39;引用的char位置。然后它增加指针的值&#39; arg&#39;和&#39; targ&#39;。

答案 2 :(得分:0)

这是您提供的功能,以功能的逐行说明进行分解。最后是修改,使功能稍微安全。

此行声明函数的返回类型。类型'void'表示此函数不返回任何内容(与函数相对的过程)。

void

此行声明函数的名称'bar',然后显示三个参数的列表,'arg','targ'和'len'。这些参数的类型是'arg'是指向字符的指针,是你在C中传递字符串的方式; 'targ'也是一个指向字符的指针,再次是你在C中传递字符串的方式; 'len'是一个int(eger)。

bar (char *arg, char *targ, int len)

此符号“{”表示函数定义的主体如下。

{

该行声明'i'是'int'(eger)类型的变量,并且该变量在函数持续时间内在堆栈上保留空间。

  int i;

此行使用'for'关键字声明重复循环。在括号之间出现三个子部分:第一部分包含循环开始之前的初始化,变量'i'设置为值0;第二部分包含一个表达式,该表达式被计算以确定是否继续循环,在这种情况下表达式'i&lt;每次遍历循环都会检查len',因此当i的值大于或等于'len'的值时,循环终止;第三部分包含一个在每个循环传递结束时执行的表达式。

  for (i = 0; i < len; i++)

这一行'{'打开for循环的语句体,

  {

这一行是执行函数的有趣工作的地方。如上所述,for循环将对列表中的每个'i'值(0,1,2,3,...,len-1)执行一次,顺序执行。注意,当'i'具有值'len'时,循环结束。请参阅上面的函数声明行,变量'arg'和'targ'是如何指向字符的?这些行取消引用这些变量以访问指针位置。

假设该行是'* targ = * arg',那么R值* arg将是'arg'所指向的当前位置的内容,而L值'* targ'将是当前的位置由'targ'指出。如果字符'x'存储在'* arg'中,则'x'将被复制到'* targ',覆盖之前的内容。

    *targ++ = *arg++;

指针之后的'++'符号不会影响指针指向的字符,但会增加指针(由于'*'和'++'运算符的优先级)。这一行可以改写为,

    *targ = *arg;
    arg++;
    targ++;

这一行'}'关闭for循环的语句体

  }

此符号'}'匹配并关闭函数定义的主体。

}

您应该始终检查函数的有效参数。您应该检查值是否超出缓冲区。我在函数中添加了一些语句,使其更加健壮。如上所述,该函数的行为类似于'memcpy',并不关心终止的'字符串';我添加了'endc'来检测字符串结束。

char* //return the destination, for further processing, e.g. strlen()
bar (char *arg, char *targ, int len)
{
  int ndx; //try searching for 'i' in a big source file sometime...
  char endc; //temporary to detect null character, more like 'strncpy'
  if( (!arg) || (!targ) || (len<1) ) return targ;
  for (ndx = 0; ndx < len; ndx++)
  {
    endc = *targ++ = *arg++;
    if( !endc ) break; //comment this out for 'memcpy' behavior
  }
}

有人会说不需要检查长度。对于简单的函数来说也是如此,但复杂的函数可能会发现这种习惯是有益的。