tonybai在他的网站上写了一个本书的知识点纲要
第五章主要讲的是指针和字符串
NULL和NUL是不同的,NULL是一个指针,通常被定义为 ((void *)0),NUL是一个ASCII码字符,用\0表示,
C语言中有两种字符串
Byte string
Consists of a sequence of char data type
Wide string
Consists of a sequence of wchar_t data type
char在C中是用int来表示的,如下:
printf("%d\n",sizeof(char)); /*打印1*/
printf("%d\n",sizeof('a')); /*打印4*/
字符串声明3种方式:
1.字面值声明
2.字符数组 char header[32];
3.指向字符的指针 char *header;
GCC使用-fwritable-strings来关闭字符串池,VS中/GF打开字符串池
字符数组也可以使用strcpy初始化如:
char header[13];
strcpy(header,"Media player");
繁琐的初始化方式:
header[0] = 'M';
header[1] = 'e';
...
header[12] = '\0';
下面的初始化方式不正确
char header[];
header = "Media player"; /*不能将字面值赋值给数组名*/
字符指针初始化方法
char *header = (char *) malloc(strlen("Media player") + 1); /*注意这里没有使用sizeof*/
strcpy(header,"Media player");
也能这样:
*(header + 0) = 'M';
*(header + 1) = 'e';
...
*(header + 12) = '\0';
作者在这里提醒:
牢记要在字符串的结尾添加字符串终止符NUL,字符串初始化使用malloc时不要使用sizeof,使用strlen函数判断字符串的长度
尝试初始化一个指向单个字符的指针不管用,如下:
char* prefix = '+'; // 不合法
因为字符值是int,如果像上面那样做就意味着将int赋值给指针类型,在解引用指针时这常常会导致程序的非正常终止
一个合法的方式如下:
char *prefix = (char *) malloc(2);
*prefix = '+';
*(prefix + 1) = 0;
标准字符串操作
字符串比较
字符串比较使用strcmp函数,不能简单使用==来判断
如:
char command[16];
printf("Enter a command:");
scanf("%s",command);
if (command == "Quit") /*这里的判断是不正确的,command是地址,这样一来只会比较字符串首地址*/
{
}
字符串拷贝
字符串连接
连接函数原型:
char *strcat(char *s1,const char *s2);注意这个函数不会自动分配内存,所以要求第一个字符串必须足够长一边容纳最终的
字符串连接结果
传递字符串
例子:
size_t stringLength(char *string) /*作者也指出这里其实最好再加个const,保证函数不会修改传进的字符串*/
{
size_t length = 0;
while(*(string++))
{
length++;
}
return length;
}
返回字符串
当函数返回字符串时返回字符串的地址,主要关注的是返回一个合法的地址
当函数返回一个字符串,它返回的字符串的地址。关注的主要问题是返回一个有效的字符串地址。
我们可以使用下面几种方式返回一个引用:
•字面值(literal)
•动态分配的内存(Dynamically allocated memory)
•局部字符串变量(A local string variable)
返回字面值地址
例子1
char* returnALiteral(int code)
{
switch(code)
{
case 100:
return "Boston Processing Center";
case 200:
return "Denver Processing Center";
case 300:
return "Atalnta Processing Center";
case 400:
return "San Jose Processing Center";
}
}
例子2
char* returnAStaticLiteral(int code,int subCode)
{
static char* bpCenter = "Boston Processing Center";/* 注意这些都是静态局部变量 */
static char* dpCenter = "Denver Processing Center";
static char* apCenter = "Atalnta Processing Center";
static char* sjpCenter = "San Jose Processing Center";
switch(code)
{
case 100:
return bpCenter;
case 135:
if(subCode < 35)
{
return dpCenter;
}
else
{
return bpCenter;
}
case 200:
return dpCenter;
case 300:
return apCenter;
case 400:
return sjpCenter;
}
}
上面例子中static可能会有问题,作者这么说“Returning a pointer to a static string used for multiple purposes can be a problem.”
然后举了个例子
char* staticFormat(const char* name,size_t quantity,size_t weight)
{
static char buffer[64]; /*假定数组范围足够大*/
sprintf(buffer,"Item: %s Quantity: %u Weight: %u",name,quantity,weight);
return buffer;
}
char* part1 = staticFormat("Axie",25,45);
char* part2 = staticFormat("Priston",55,5);
printf("%s\n",part1);
printf("%s\n",part2);
代码运行后输出结果如下:
Item: Piston Quantity: 55 Weight: 5
Item: Piston Quantity: 55 Weight: 5
作者在这里解释了原因,由于两次调用都是用的同一个静态buffer,最后一次的调用覆盖了前一次的调用结果
返回动态分配的内存
char* blanks(int number)
{
char* spaces = (char*) malloc(number + 1);
int i;
for (i = 0; i < number; i++)
{
spaces[i] = ' ';
}
spaces[number] = '\0';
return spaces;
}
作者在这里指出需要函数调用者来解引用返回的内存,否则会产生内存泄露原文如下:
It is the function’s caller’s responsibility to deallocate the memory returned. Failure to
deallocate it when it is no longer needed will result in a memory leak.
举例:
printf("[%s]\n",blanks(5));这就造成了内存泄露,因为blanks函数返回的指针没有保存也没有释放,造成泄露
这样一改进,就不会有内存泄露了
char* tmp = blanks(5);
printf("[%s]\n", tmp);
free(tmp);
返回局部字符串变量地址
这个会造成问题,因为内存会被稍后的栈帧破坏掉,应该避免这种方式,作者在这里举了个例子
函数指针和字符串
作者在这里举了一个例子也证明函数指针和字符串结合操作给程序带来的灵活性
int compare(const char* s1,const char* s2)
{
return strcmp(s1,s2);
}
int compareIgnoreCase(const char* s1,const char* s2)
{
char* t1 = stringToLower(s1);
char* t2 = stringToLower(s2);
int result = strcmp(t1,t2);
free(t1);
free(t2);
return result;
}
char* stringToLower(const char* string)
{
char* tmp = (char *) malloc(strlen(string) + 1);
char* start = tmp;
while(*string != 0)
{
*tmp++ = tolower(*string++);
}
*tmp = 0;
return start;
}
使用typedef来定义将要使用的函数指针
typedef int (fptrOperation)(const char*,const char*);
void sort(char *array[],int size,fptrOperation operation)
{
int swap = 1;
while(swap)
{
swap = 0;
int i;
for (i = 0; i < size - 1; i++)
{
if (operation(array[i],array[i]) > 0)
{
swap = 1;
char *tmp = array[i];
array[i] = array[i + 1];
array[i + 1] = tmp;
}
}
}
}
void displayNames(char* names[],int size)
{
int i;
for(i = 0; i < size; i++)
{
printf("%s ", names[i]);
}
printf("\n");
}
char* names[] = {"Bob","Ted","Carol","Alice","alice"};
sort(names,5,compare);
displayNames(names,5);