C语言字符串

一、字符串结束标志

在讨论C语言中缓冲区溢出问题时,很多溢出问题是由字符串和字符串的相关库函数引起的,涉及最多的就是字符串结尾标识符’’,所以,C语言的字符串为什么以0作为它的结尾?

因为c语言中没有字符串类型,所以借助字符数组来存储字符串,为了区别字符串,就需要特殊标记。而在一般的字符集中,0一般保留不用,因此C用0来作为字符串的结束标志,它占用存储空间,但不记入字符串的实际长度。所以我们用sizeof关键字和strlen计算同一个字符串所得结果相差1,这个1就是结束标志0.字符串长度和所占内存大小例子程序:

void testString()
{
    //initial string in different way 
    //and then test their length and memory storage volume
    char str1[] = "i love you";
    char * str2 = "i love you";
    char str3[] = { 'i',' ','l','o','v','e',' ','y','o','u'};

    printf("str1: %d %d
", strlen(str1), sizeof(str1));
    printf("str2: %d %d
", strlen(str2), sizeof(str2));
    //输出 10 4
    //我明白4是指针占4字节的内存,所以我就改为
    printf("str2: %d %d
", strlen(str2), sizeof(*str2));
    //输出 10 1
    //我就奇怪了,怎么还输出1了
    //*str2是str2所指向的位置的内容,的确是一个字符,没有错
    //如果要指向下一个字符,还需要移动指针。。。
    printf("str2: %d %d
", strlen(str2), sizeof("i love you"));
    //ooutput 10 11
    printf("str3: %d %d
", strlen(str3), sizeof(str3));
    //第一次输出42 10; 第二次输出23 10
    //开始,我以为是怎么回事呢,最后才明白,这就是栈溢出,这就是漏洞
    //str3是一个存储字符的数组,但是它不是一个字符串常量,所以不会有自动加‘’的操作
    //所以当我们将str3用strlen遍历计数时,就会一直读出数组之外,直到遇到‘’
    //所以在实时运行时才会因为内存内容的变化,输出不同的数字
    //所以对于没有‘’做结束的字符数组,我们是不能使用像strlen这样用于字符串的函数的
    //而应该使用length = zizeof(str3)/sizeof(char);
}

二、字符串和内存块复制函数

1. strcpy()

头文件:#include <string.h>

函数原型:char *strcpy(char *dest, const char *src);

函数说明:把从src地址开始且含有''结束符的字符串赋值到以dest开始的地址空间

返回值:返回参数dest 的字符串起始地址。

附加说明:如果参数 dest 所指的内存空间不够大,可能会造成缓冲溢出(buffer Overflow)的错误情况,在编写程序时请特别留意,或者用strncpy()来取代。

该函数的参数是字符指针,也就是可以是字符串变量和字符数组,因为它们的变量名代表首字符地址。字符串默认有一个''结束符,字符数组没有。所以此处需要注意:因为src要求有''结束符,所以字符数组的长度必须大于等于src包含''结束符的总长度。例如,char* src="abcd"; char dest[5]; 这里dest的长度就至少为5。

2. strncpy()

原型:char * strncpy(char *dest, char *src, size_t n);  

功能:将字符串src中最多n个字符复制到字符数组dest中,它并不像strcpy一样遇到''就停止复制,而是在遇到’’后如果不足n个字符,就会将后面全部置为0,直到凑够n个字节。如果在遇到’’前就凑够n个字符,也结束复制,这时候因为末尾没有’’结束符,在读取时就会产生缓冲区溢出错误。

下面是一个比较以上两个函数的例子:

//这个函数是为了测试strcpy和strncpy的不同之处
//前者将src复制到dest,遇到‘’就停止
//后者遇到''后,将后面所有都置为''
//所以看到,str3后面会有原先初始化的A,但是str2在字符串结束后,全部是空格
int testStrncpy()
{
    char str1[] = "To be or not to be";  //18 bytes without ''
    char str2[40];
    char str3[40];
    memset(str2, 65, 40);
    memset(str3, 65, 40);
    /*for (int i = 0; i < 40; i++)
    {
        str2[i] = 'a';
        str3[i] = 'a';
    }*/
    /* copy to sized buffer (overflow safe): */
    strncpy(str2, str1, sizeof(str2));
    strcpy(str3, str1);
    printf("str2:
");
    for (int i = 0; i < 40; i++)
    {
        printf("%c", str2[i]);
    }
    printf("str3:");
    for (int i = 0; i < 40; i++)
    {
        printf("%c", str3[i]);
    }

    /* partial copy (only 5 chars): */
    //strncpy(str3, str2, 5);
    //str3[5] = '';   /* null character manually added */

    //puts(str1);
    //puts(str2);
    //puts(str3);

    return 0;
}

3. memcpy()

头文件:#include<string.h>

函数原型:void *memcpy(void* dest, const void *src, size_t n);

返回值:函数返回一个指向dest的指针。

功能:由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内。

说明:

1.source和destination所指内存区域不能重叠,函数返回指向destination的指针。

2.与strcpy相比,memcpy并不是遇到''就结束,而是一定会拷贝完n个字节。memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例:

char a[100], b[50];
memcpy(b, a,sizeof(b)); //注意如用sizeof(a),会造成b的内存地址溢出。
//strcpy就只能拷贝字符串了,它遇到''就结束拷贝;例:
char a[100], b[50];
strcpy(a,b);

3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。

注意,source和destin都不一定是数组,任意的可读写的空间均可。

下面是三个例子程序:

int testMemcpy()
{
    char* s = "Golden Global View";  //19 bytes if added '/0'
    printf("%d
", strlen(s));       //output:18
    char d[20];
    memcpy(d, s, strlen(s));
    d[strlen(s)] = '';//因为从d[0]开始复制,总长度为strlen(s),d[strlen(s)]置为结束符
    printf("%s", d);
    return 0;
}
int laterMemcpy()
{
    char*s = "Golden Global View";
    char d[20];
    memcpy(d, s + 14, 4);//从第14个字符(V)开始复制,连续复制4个字符(View)
    //memcpy(d,s+14*sizeof(char),4*sizeof(char));也可
    d[4] = '';
    printf("%s
", d);
    return 0;
}

int coverMemcpy()
{
    char src[] = "******************************";
    char dest[] = "abcdefghijlkmnopqrstuvwxyz0123as6";
    printf("destinationbefore memcpy: %s
", dest);
    memcpy(dest, src, strlen(src));
    printf("destinationafter memcpy: %s
", dest);
    return 0;
}

 4. memmove()

 void * memmove ( void * destination, const void * source, size_t num );

功能等基本和memcpy相同,但它允许src和destination重叠(overlap)  例如:

/* memmove example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "memmove can be very useful......";
  memmove (str+20,str+15,11);
  puts (str);
  return 0;
}
//output:
//memmove can be very very useful.

三、总结

1.strcpy是依据’’作为结束判断的,如果destination的空间不够,则会引起 buffer overflow。

2.strncpy是依据’’和n作为结束判断的,destination一定会被写入n个字符,n个字符是否含有字符串结束标志,取决于遇到src中’’时是否筹够了n个字符。

3.memcpy用来在内存中复制数据,由于字符串是以“”结尾的,所以对于在数据中包含“”的数据只能用memcpy。strcpy和strncpy都不能完整复制数据。

4.strncpy和memcpy很相似,只不过它在’’处停止。

所以总的来说使用时注意:

1.destination指向的空间要足够拷贝;使用strcpy时,destination指向的空间要大于等于src指向的空间;使用strncpy或memcpy时,destination指向的空间要大于或等于n。

2.使用strncpy或memcpy时,n应该大于strlen(src),或者说最好n >= strlen(src)+1;这个1 就是最后的’’。

3.使用strncpy时,确保destination的最后一个字符是’’。

 

 

 

参考文章:

http://blog.csdn.net/tigerjibo/article/details/6841531

http://c.biancheng.net/cpp/html/164.html

http://www.cplusplus.com/reference/cstring/strncpy/

http://blog.csdn.net/taric_ma/article/details/7383713

 

 

 

原文地址:https://www.cnblogs.com/wangyuxia/p/6719381.html