我试图用偏移量为2的第二个字符串覆盖第一个字符串的一部分.

这是我的第二个有效的try :

#include <stdio.h>
#include <string.h>

int main() {
   char s[] = "Hello, World!";
   char a[] = "aaa";

   printf("%d\n", &s);
   printf("%d\n", &s[2]);

   memcpy(&s[2], a, sizeof(a)-1);
   printf("%s\n", s);
   return 0;
}
$ gcc -o test test2.c && ./test 
520783106
520783108
Heaaa, World!

下面是我的第一次try ,它不起作用,在编译和段错误时发出警告:

#include <stdio.h>
#include <string.h>

int main() {
   char s[] = "Hello, World!";
   char a[] = "aaa";

   printf("%d\n", &s);
   printf("%d\n", sizeof(char)*2);
   printf("%d\n", &s + (sizeof(char)*2));

   memcpy(&s + (sizeof(char)*2), a, sizeof(a)-1);
   printf("%s\n", s);
   return 0;
}
$ gcc -o test test1.c && ./test 
test1.c: In function ‘main’:
test1.c:12:4: warning: ‘memcpy’ writing 3 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
   12 |    memcpy(&s + (sizeof(char)*2), a, sizeof(a)-1);
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test1.c:5:9: note: at offset 28 into destination object ‘s’ of size 14
    5 |    char s[] = "Hello, World!";
      |         ^
1547566642
2
1547566670
Hello, World!
Segmentation fault

谁能给我解释一下为什么Test1不起作用?

为什么1547566642和2的和等于1547566670而不是1547566644?

我想这是一个类型问题.SIZOF的回归是SIZE_T,但S是什么类型的?

首先要感谢您的帮助.;)

推荐答案

您的示例变得有点复杂,很可能是偶然的.您需要重点了解的是"数组衰减",这意味着无论何时在大多数表达式中使用数组,它都会"衰减"为指向其第一个元素的指针.

例如,在表达式s[2]中,s衰减为指向char的指针.[]运算符被定义为s[2]%等同于指针算术,这意味着s[2]等同于写入*(s+2)(但前者最好,因为它更具可读性).在这两种情况下,最终处理的都是指针而不是array.

现在有一些例外,当数组衰变不发生时,你们在这里遇到了其中的一些.当在数组上使用Address-Of运算符时,不会发生衰减.因此,&s不会给出第一个元素的地址,而是数组本身的地址.尽管数组和它的第一个元素都有相同的地址,但无论打印&s还是&s[0],都会得到相同的地址.但它们是不同类型的.在本例中,&s给出的是指向char (*)[14]类型数组的指针,但&s[0]给出的是纯char*.

现在,当您开始进行指针算术时,这些类型很重要,因为当您这样做时,加法是以sizeof(pointed-at-item)为基础工作的.因此&s + 2给出2*14=28字节的增加,而&s[0] + 2给出2字节的增加.前者在这里显然是错误的.

数组衰减的另一个例外是sizeof运算符--正如您注意到的,您可以对数组使用它,并以字节为单位获取数组大小.它在衰减/指向数组上不起作用,所以如果我们做char* ptr = s; ... sizeof(ptr),我们得到的不是数组的大小,而是指针本身,这可能没有什么用处.


其他值得注意的事情:

  • 根据定义,sizeof(char)总是1,所以没有必要将任何东西与sizeof(char)相乘--这只会增加代码的杂乱度.
  • 打印带有printf的地址时,您应该使用%p.部分原因是%d可能不够大,不足以正确表示给定系统上的地址(例如,它不在x86_64上),部分原因是地址总是以十六进制写入,而%p负责这一点.从学究的Angular 来看,我们还应该将指针指向(void*),因此printf("%p\n", (void*)&s[0]);将是打印第一项地址的最正确方式.

C++相关问答推荐

在x86汇编中,为什么当分子来自RDRAND时DIV会引发异常?

问关于C中的指针和数组

sizeof结果是否依赖于字符串的声明?

为什么可以通过指向常量int的指针间接地改变整数的值?

为什么输出不是从上到下C

为什么在此程序中必须使用Volatile关键字?

将fget()与strcMP()一起使用不是正确的比较

Rust FFI--如何用给出返回引用的迭代器包装C风格的迭代器?

平均程序编译,但结果不好

致命错误:ASM/rwan ce.h:没有这样的文件或目录.符号链接还不够

';\n&39;和';\r&39;中的';\n&39;之间有什么关系?

链表删除 node 错误

C";中的ANN运行时判断失败#2-变量outputLayer;周围的堆栈已损坏.运行后出错

即使我在C++中空闲,也肯定会丢失内存

C: NULL>;NULL总是false?

令人困惑的返回和 scanf 问题相关

为什么 Linux 共享库 .so 在内存中可能比在磁盘上大?

C23 中的 [[reproducible]] 和 [[unsequenced]] 属性是什么?什么时候应该使用它们?

无法在 C 中打开文本文件,我想从中读取文本作为数据并将其写入数组

int 与 size_t 与 long