1.strlen 用来计算一个指定的字符串的长度
NAME
strlen - calculate the length of a string
计算一个指定的字符串的长度
SYNOPSIS
#include <string.h>
size_t strlen(const char *s); //函数原型
s:字符指针,指向你要计算长度的字符串的首地址
返回值:
返回s指向的字符串的长度
DESCRIPTION
The strlen() function calculates the length of the string pointed to by s, excluding(排除/不包括) the terminating null byte ('\0').
RETURN VALUE
The strlen() function returns the number of characters in the string pointed to by s.
例子:
char s[] = {"1234567"};
sizeof(s) == 8
l = strlen(s+3);
l = ? 4
char s1[4] = {'A','B'};
l = strlen(s1);
l = ? 2
char s2[4] = {'A','B','C','D'};
l = strlen(s2);
l = ? 不确定
2.strcpy/strncpy 复制一个字符串(string copy)
NAME
strcpy, strncpy - copy a string
SYNOPSIS
#include <string.h>
字符串拷贝函数,把一个字符串拷贝到另一个字符串中去
strcpy用来把src指向的字符串,拷贝到dest指向的内存空间中去
一个字符一个字符的拷贝,直到遇到第一个'\0'(如果不遇到'\0',就一直copy)
'\0'也会被复制
char *strcpy(char *dest, const char *src);
dest:指向目的地地址
src:指向要复制的字符串的首地址
const char *src:保证不会通过src这个指针去修改src指向的内容
增强程序的健壮性
返回值:
返回一个指针类型(地址编号)
返回拷贝后的dest的首地址
例子:
char s[6]; //sizeof(s) == 6,strlen(s) == 不确定
char *p;
p = strcpy(s,"123456"); //返回dest的地址,dest的就是s的地址,p就指向了s
printf("s = %s\n",s);
printf("p = %s\n",p);
====================================================
char s[10] = {"ABCDEFGHIJ"};
char *p;
p = strcpy(s,"123456"); //返回dest的地址,dest的就是s的地址,p就指向了s
printf("s = %s\n",s);
printf("p = %s\n",p);
====================================================
char *q; //野指针,q指向的内存区域不一定可用
//strcpy(q,"123"); //很有可能会有段错误,q指向的内存区域不一定可用
//q指向的地方没有一块可以的空间.
//printf("%s\n",q);
====================================================
char s1[8];
char s2[8] = {"abcdefg"};
//一般的编译器会把s1和s2放到连续的空间
printf("s1 = %p\n",s1);
printf("s2 = %p\n",s2);
strcpy(s1,"1234567890");
printf("s1 = %s\n",s1);//s1 = 1234567890
printf("s2 = %s\n",s2);//s2 = 90
从上面的结果可以看出,strcpy的设计者在设计strcpy的时,可能喝了一点假酒,写了一个有bug的程序,strcpy可能会破坏dest后面的内存,为了修改这个错误,设计了一个strncpy
strncpy的功能和strcpy类型,只不过它最多拷贝src前面的n(一般会指定dest的最大可用空间)个字符,那么strncpy到底会copy多少个字符呢?
1.遇到了\0会结束copy,'\0'也会被复制
2.已经拷贝了n个字符,strncpy也会结束(\0不会被自动copy,除非\0是第n个字符)
不管什么情况,最多是n个字符
char s[10];
strncpy(s,"0123456789abcdefg",10);
char *strncpy(char *dest, const char *src, size_t n);
dest:指向目的地地址
src:指向要复制的字符串的首地址
n:最多copyn个字符(一般会指定dest的最大可用空间)
返回值:
返回一个指针类型(地址编号)
返回拷贝后的dest的首地址
3.strcmp/strncmp 比较两个字符串是否相等
NAME
strcmp, strncmp - compare two strings
比较两个字符串
SYNOPSIS
#include <string.h>
int strcmp(const char *s1, const char *s2);
s1:要比较的第一个字符串的地址
s2:要比较的第二个字符串的地址
字符串是如何比较的?
逐个字符依次比较(比较每一个字符的ASCII码)
一个字符一个字符的比较
返回值:
The strcmp() and strncmp() functions return an integer less than(小于), equal to(等于), or greater than(大于) zero
if s1(or the first n bytes thereof) is found, respectively, to be less than, to match, or be greater than s2.
如果发现s1(或其前n个字节)分别小于(返回<0),等于(返回==0)或大于s2(返回>0)。
if(c1 > c2) //返回 >0
{
不同的标准实现方式不一样,
有的是返回1
有的返回ASCII码的差值
}
if(c1 < c2) 返回 <0
有可能是-1
if(c1 == c2) 继续比较下一个字符,直到都同时遇到\0,返回0
strcmp("123","ABC"); -----> <0
strcmp("123","12A"); -----> <0
strcmp("123","123\0ABC"); -----> ==0
strcmp("1234","123"); ----> >0
char s1[10] = {...}
char s2[10] = {...}
strcmp(s1,s2); //按照上面的规则对照即可
strncmp和strcmp类似,用来比较s1和s2前面的n个字符是否相等
int strncmp(const char *s1, const char *s2, size_t n);
strncmp(s1,"ABC",3);
//验证s1前面三个字符是否为ABC
4.strcat/strncat 字符串连接函数
NAME
strcat, strncat - concatenate two strings
连接两个字符串
SYNOPSIS
#include <string.h>
strcat用来把src指向的字符串拷贝(连接)到dest指向的字符串的末尾(连接到末尾)
char *strcat(char *dest, const char *src);
dest:目标字符串,指向一段可用的空间
空间应该足够大,至少能放得下src指向的字符串
src:被拷贝的字符串
返回值:
如果成功,返回连接后的字符串的首地址(dest)
如果失败,返回NULL(空指针,在C语言中的即编号为0的地址)
//访问(读/写)空指针都会造成段错误
例子:
char s1[12] = {"ABCDEFG"};
strcat(s1,"12345");
先去掉dest后面的\0
在把src指向的字符串复制过去
s1:ABCDEFG12345
上面的函数可能会造成内存越界,strncat是用来修复strcat的那一个bug的
char *strncat(char *dest, const char *src, size_t n);
strncat把src指向的字符串拷贝到dest的末尾,但是它最多拷贝n个字符,
strncat到底会copy多少个字符呢?
1.遇到了\0会结束copy,'\0'也会被复制
2.已经拷贝了n个字符,strncat也会结束(\0不会被自动copy,除非\0是第n个字符)
不管什么情况,最多是n个字符
12.函数与指针
在C语言中,函数也是有地址的,既然函数有地址,是不是可以定义一个变量去保存函数的地址呢?
肯定可以,
指针变量,保存函数的地址(指向函数的指针--函数指针)
1.函数指针如何定义
指向的对象的类型 *指针变量名;
int sum(int a,int b)
{
printf("i am sum\n");
return a+b;
}
expected ‘char *’ but argument is of type ‘int (*)(int, int)’
&sum的类型是 int (*)(int, int)
假设我要定义一个指针变量p,保存sum函数的地址,怎么定义?
指向的对象的类型 *p;
要保存sum的地址,指向的类型就是sum的类型
typeof(sum) *p;
typeof(sum):如何描述一个函数的类型呢?
函数的三要素:
返回值类型 函数名(函数的参数类型列表);
int sum(int a,int b);
//sum是int (int,int)类型的函数(把名字去掉就是对象的类型)
//有一个int的返回值,有两个int类型的参数的函数
typeof(sum) ====>int (int,int);
======>
int (int,int) *p; //C语言中括号不能和关键字写在以前
======>
int *p(int,int); //编译器会把这个东西认为是函数的声明
//声明了一个函数p,有一个int *的返回值,有两个int类型的参数
//指针函数:返回值为指针的函数
括号的优先级比*高,所有正确的定义方法:
======>
int (*p)(int,int); //函数指针类型,
//把名字去掉就是对象的类型,p的类型是int (*)(int,int)
(*p)说明p是一个指针类型,可以保存一个int (int,int)类型的函数的地址。
每一个函数都是有类型的,如果类型不匹配,就会报错:
void f(int a,double b);
typeof(f) --->void (int,double)
void f(void);
typeof(f) --->void (void)
void *f(int *p);
typeof(f) --->void *(int *)
如果需要定义一个函数指针p,指向上面的函数(保存上面的函数的地址)。
void *(int *) *p;
---->
void *(*p)(int *);
总结,函数指针的定义方法:
指向的函数的返回值类型 (*指针变量名)(函数的参数类型列表);
例子:
void swap(int *pa,int *pb);
要定义一指针变量p,来保存swap的地址
void (*p)(int *,int *);
//p就可以保存一个返回值为void有两个int*参数的函数的地址
p还没有赋值(p是一个野指针),不确定指向的指针就是野指针
2.函数指针如何赋值
p = 函数的地址;
函数的地址如何获取呢?
&函数名
or
函数名
在C语言中,函数名就代表函数的首地址(入口地址)
=====》
p = ∑
or
p = sum;
(只有当sum为函数时,sum和&sum才一样)
函数指针已经保存了函数的地址,能不能通过p去调用这个函数呢?
肯定可以
3.如何通过函数指针去调用函数呢?
在前面已经让p指向了函数(保存了函数的地址)
取地址对应的对象使用 *
p(3,5); //只有当p是函数指针才可以这样
or
(*p)(3,5);
*p(3,5); //ERROR
只有当p是函数指针时,
*p <-----> p
如果指向的函数没有参数呢?
p();
or
(*p)();
通过函数指针调用函数有两种方式:
1.(*函数指针变量名)(实参列表);
2.函数指针变量名(实参列表);
4.函数指针在C语言中的意义
实现C语言的回调函数。
在调用同一个函数的时候,根据函数的走向可以决定是否调用另一个函数
本质:
就是把函数A的地址当做参数传递到拎一个函数B中去,另一个函数可以通过传递进来的地址去调用函数A,
函数的地址当做函数的参数。
例子:
13.二级指针和多级指针
int a; //定义了一个变量a,分配了4字节空间
可以定义一个指针变量p,来保存a的地址
typeof(a) *p; //指向的类型 * 指针变量名;
int *p;
p = &a; //p保存了a的地址,指向了a
p也是一个变量,OS也为它分配了空间,分配了8字节空间,空间里面存储的是a的地址
p的8字节空间,是不是本身也有地址,我们可不可以定义一个指针变量p2来保存p的地址呢?
肯定可以.
如果可以,p2应该是什么类型呢?
p的地址为&p,把&p赋值给p2
p2 = &p;
赋值符号两边的类型应该一致
&p的类型就是p2的类型
&p:肯定是一个地址,保存了p的地址,指向了p
&p---->typeof(p) *----->int **;
typeof(p) *p2; //p的类型是int*
====>
int **p2;
//p2就是我们所说的二级指针,它保存了一个一级指针的地址
p2 = &p; //p2保存了p的地址
p2 = &a; //ERROR
p2:int**
&a:int *
sizeof(p2) == 8; //所有的指针都是8字节
可以通过p2去访问a吗?
肯定可以(知道一个地址就可以访问内存)
如何访问?
*p2 ----> *&p ---->p ----->&a
*p -----> *&a ---->a
**p2---->**&p----->*p
**p2 = 1025; //a = 1025;
p2也是一个变量,也有自己的内存空间和编号,是不是可以定义一个变量p3去保存p2的地址呢?
肯定可以
p3 = &p2;
p3是什么类型呢?
typeof(p2) *
=====>
int ***p3; //三级指针
p3也是一个变量.......
二级指针和多级指针比较绕,分不清的话,不要管它是几级指针,只要知道它是一个指针就可以了
并且知道保存了谁的地址