==========
指针二
==========
【回顾昨天】
1、认识指针
地址 编号 常量
指针 变量
2、定义指针
所指类型 * 变量名
3、给指针变量进行初始化【&】
【注】给指针变量进行初始化只可以赋值地址。
4、指针所占的字节数
64位系统下面都是8字节。
5、为什么使用指针?
为了跨栈访问数据。
【补充】当函数被调用时,cpu会在栈区给该函数开辟一块空间。当函数 结束调用时,该空间被摧毁。
6、指针与数组
指针 + 1 所指类型的字节
数组名 + 1 数组元素的字节数
数组名—代表着数组首元素的地址; int a[10]; a == &a[0]
*(a+1)—>*&a[1]—>a[1]
int * p = &a[0] *(p+1)—>*&a[1]—>p[1]
【结论】
1、数组名代表着数组首元素的地址。是一个常量。
int a[10]; a++; 常量不可以加加 数组名是一个常量
2 、指针指向数组首元素时,可以使用下标法遍历数组。
int *p = a(=&a[0]) p[i] 3、a代表着数组首元素的地址;&a代表着数组的地址。
这两个地址的值是一样的,但是a+1的结果与 &a+1的结果是不一 样的。
为什么要使用指向数组的指针?有指向数组首元素的指针不是就完全可 以吗?
对于指向数组的指针,主要是用在二维数组传参时。
二维数组的首元素是一个数组。所以将二维数组的数组名作为实参,那 么形参需要是一个指向数组的指针。
7、定义一个指向数组的指针
int (*p)[10]
8、指向指针的指针
int * q;
int **p = &q;
9、指向函数的指针
返回值类型 (*p)(形参列表) = 函数名;
【见代码 指针函数的应用】
【目标】
1、定义一个指向函数的指针;
2、给函数指针进行初始化;赋值的是函数名。
3、通过函数名可以调用函数;通过指向函数的指针也可以调用函数。
4、函数名代表着函数的入口地址。
【扩展】函数的入口地址
我们编辑的文件,在编译之后会变成二进制文件,保存在硬盘上,当程 序运行的时候,会将该二进制文件读取到常量区中。二进制文件就是 cpu的使用说明书。函数的入口地址就相当于是这个说明中该函数所在 的页数(地址)。当发生函数调用的时候,cpu会根据函数名找到到函 数的地址,然后读取指令,进行操作。
【注】函数名代表着入口地址,并不是函数在运行阶段发生调用时候的 函数栈的地址。
一个程序经历编辑(coding)、编译(command+b)、运行的过 程。
10、返回指针的函数
【见代码 函数指针的应用】
11、认识复杂的类型
【如何辨别与指针相关的妖魔鬼怪?】
1、先找到变量名,
2、然后观察左右两边的运算符,() [] *
3、根据优先级去确定这到底是个什么鬼;优先级最高的运算符如果是,说明这是一个 指针;优先级最高的是[],说明这是一个数组;优先级最高的是(),说明这是一个 函数的声明。
4、如果是数组,我们要看数组元素是什么类型,遮住数组名与数组的大小剩余部分就 是数组元素的类型;
如果是指针,我们要看指针指向什么类型,遮住变量名与剩余部分就所指向的类 型。
如果是函数,我们要看函数的返回值类型与形参列表。函数名前半部分都是返回值 类型,函数名后面都是形参列表;
练习
int (*p[10])[10] 数组
int (*p[10])[10] 数组元素是指针
int (*p[10])[10] 指针是指向数组的
这是一个数组,数组元素是指向数组的指针
int (*p[10])(int) 数组
int (*p[10])(int)数组元素是指针
int (*p[10])(int)指向函数
这是一个数组,数组元素是指向函数的指针
int *(*p)[10]; 指针
int *(*p)[10] 指向数组
int *(*p)[1 数组元素是指针
int *(*func[10])(int,int); 数组
int *(*func[10])(int,int);数组元素是指针
int *(*func[10])(int,int);指向的是函数,函数的返回值int * 形参列表(int,int);
int (*p[10])(int) 数组
int (*p[10])(int)数组元素值指针
int (*p[10])(int)指向函数 函数返回值int 形参int
int *(*p)[10]; 指针,
int *(*p)[10];指向数组
int *(*p)[10];数组元素是指针
int *(*func[10])(int,int);数组
int *(*func[10])(int,int);数组元素是指针
int *(*func[10])(int,int);指向函数,函数返回值int *,形参 (int,int)
12、特殊的指针—空指针 与 野指针
当我们声明一个指针变量时,不给指针变量赋值,那么此时这个指针就 是一个野指针,他的指向是不明确的。在程序使用野指针是非常危险 的。
当我们暂时无法给一个指针变量进行赋值时,我们可以将其置为NULL, 即:int * p = NULL;
13、const常量修饰符与指针的关系
int * const p = &b;
p = &c;//错误!
//const放在*后面,修饰的是指针,即指针的指向不允许发生改变。
const int * p = &b; int const * p = &b;
// *p = 100; 错误!
//const放在*前面,修饰的是指针的指向。也就是说不能通过指针去修改所指变 量的内容。
=============
字符串
=============
一、认识字符串
字符串是由双引号括起来的一串字符。例如:”abcdefg””123456””+”
【注】
1、每一个字符串末尾都有一个默认的字符串结束标志。’\0’.对应ASCII 码是0。
2、字符串的结束标志不需要手动添加,系统会自动在字符串结尾的部分加 上字符串结束标志。
3、在C语言中没有一种类型是用来存储字符串。他是将字符串看成字符数 组存储。
4、在oc语言中NSString是字符串的类型。
二、认识字符数组—字符数组是用来存储字符串的。
char a[20]; //声明了一个字符数组
1、使用单个字符对字符数组进行初始化;
2、使用字符串对字符数组进行初始化;
【注】
1、字符串是用字符数组来存储的;
2、字符数组定义的容量一般要比字符串稍微大一些。
3、字符串的格式说明符是%s,%s后面输出列表中对应的字符数组的数组 名。
三、有关字符串的函数
1、有关字符串的输入与输出函数
scanf("%s",a);
printf("%s\n",a);
缺点: 使用scanf输入字符串时,字符串之间不允许输入空白字符(空格、tab、回车)
—————————————————————————————————————————————
//使用gets puts
//输入一个字符串到内存中
gets(a);
//输出一个字符串
puts(a);
gets 缺点:不会进行越界判断
—————————————————————————————————————————————
//fgets fputs
//参数1:字符数组的地址
//参数2:输入的字节大小
//参数3:标准输入流
fgets(a, sizeof(a), stdin);
a[strlen(a)-1] = '\0';//消除最后读进来的换行符
//参数1:数组名
//参数2:标准输出流
fputs(a, stdout);
缺点:fgets输入时会自动在最后一个字符上添加一个'\0',缺点:如果输入的字符串小于输入 字节的大小,那么会将最后的'\n'读取进来。
2、strlen
函数原型:unsigned long strlen(const char *);
函数作用: 计算字符串的长度,不带最后结束标志。
【课堂练习】自定函数,计算字符串的长度,不允许使用官方的方法。
unsigned long myStrlen(const char *);
3、strcpy
函数原型: strcpy(dest, src)
函数作用:将src的字符串拷贝到dest的空间中,同时会将src的字符串结 束标志拷贝过去。
【课堂练习】自定函数,实现字符串拷贝,不允许使用官方的方法。
void myStrCpy(char dest,const char src);
4、strcmp
函数原型:int strcmp(const char *, const char *);
函数作用:字符串的比较 字符1与字符串2逐个字符比较,当出现不同的字符时, 两个字符相减(ASCII相减)的结果就是字符串的比较结果。
【课堂练习】自定函数,实现字符串比较,不允许使用官方的方法。
int myStrCmp(const char *str1,const char* str2);
5、strcat
函数原型:char *strcat(char *str1, const char *str2);
函数作用:将str1与字符串str2拼接起来;
【注】1、str1的空间要足够大;
2、str2可以是一个常量字符串,也可以是一个字符数组。
【课堂练习】自定函数,实现字符串拼接,不允许使用官方的方法。
void myStrCat( char *str1,const char* str2);
6、strchr
函数原型:char *strchr(const char *, int);
函数作用:在字符串中查找字符,返回字符第一次出现的位置。
【课堂练习】自定函数,实现字符串查找字符,不允许使用官方的方法。
char* myStrChr( char str1,const char str2)
7、strstr
函数原型:char *strstr(const char *, const char *);
函数作用:在字符串中查找子串,返回该子串第一次出现的位置。
【课堂练习】自定函数,实现字符串查找子串,不允许使用官方的方法。
char* myStrStr( char str1,const char str2)
8、编外函数
函数原型:int atoi(const char *);
函数作用:将字符串转换成int数据,转换规则:从第一个非空白字符 起,如果第一个非空白字符是数字或者正负号开始转换直到碰 到第一个非数字字符停止转换。如果第一个非空白字符不是数 字或不是正负号则转换失败,结果为0.
double atof(const char *);
long atol(const char *);
【有关字符的函数】
int isalnum(int);//判断一个字符是否是数字字符或者是字母字符,是,返回1,不是返回0
int isalpha(int);//判断一个字符是否是字母
int islower(int);//判断是否是一个小写字母
int isupper(int);//判断是否是大写字母
int tolower(int);//将大写转换成小写
int toupper(int);//将小写转成大写
指针的复习
#include
int main(int argc, const char * argv[]) {
int a[10];
//数组首元素的地址
printf("%p\n",a);
//数组的地址
printf("%p\n",&a);
//数组首元素地址+1 4个字节
printf("%p\n",a+1);
//数组地址+1 40个字节
printf("%p\n",&a+1);
return 0;
}
返回指针的函数
#include
#include
int * func();
int main(int argc, const char * argv[]) {
int * p = func();
int * r = (int*)malloc(sizeof(int)*10);
for(int i = 0;i<10;i++){
r[i] = i*1111;
printf("%p\n",&r[i]);
}
//func函数调用结束之后,func栈空间被摧毁,数组a也就不存在了。
for (int i = 0; i < 10; i++) {
printf("%d ",p[i]);
}
return 0;
}
int * func(){
//在堆上开辟4*10个字节的空间,malloc是程序员手动开辟空间的方法。返回值是一个地址。
//在堆上开辟的空间需要程序员手动去释放,只有调用free函数才会被摧毁。
int * p = (int*)malloc(sizeof(int)*10);
for(int i = 0;i<10;i++){
p[i] = i*10;
printf("%p\n",&p[i]);
}
// free(p);
return p;
//a在func函数栈开辟空间
// int a[10] = {1,2,3,4,5,6,7,8,9,10};
// return a;
}
函数指针应用
#include
int test(int (*p)(int a,int b),int a,int b);
int gcd(int a,int b);
int lcm(int a,int b);
int main(int argc, const char * argv[]) {
int a,b,c;
printf("请输入a,b,c的值\n");
scanf("%d%d%d",&a,&b,&c);
if (c>0) {
//求a与b的最大公约数
printf("%d\n",test(gcd,a,b));
}else{
//求a与b的最小公倍数
printf("%d\n",test(lcm,a,b));
}
//函数名代表着函数的入口地址
printf("+++++%p\n",test);
printf("+++++%p\n",&a);
// 【oc阶段】回调。
// [btn addtarget:withSelector:]
return 0;
}
int test(int (*p)(int a,int ),int a,int b){
return p(a,b);
}
int gcd(int a,int b){
if (a
认识字符串
#include
int main(int argc, const char * argv[]) {
// insert code here...
//printf格式化输出碰到'\0'字符串结束标志符,就认为要打印的字符串输出完毕。
//'\0'字符串结束标志 空操作符
//' '空格字符 可打印的字符
printf("abcdefg\0hijklmn");
printf("Hello, World!\n");
return 0;
}
野指针与空指针
#include
int main(int argc, const char * argv[]) {
//在Xcode编译器中会将没有初始化的数据,初始化为默认的值。
//int初始化为0,float初始化为0.0 指针初始化为NULL
//当我们声明一个指针,暂时没有办法为其初始化,我们将置为NULL.
//否则的话就是野指针。
int * p = NULL;
printf("%p\n",p);
return 0;
}
字符串的函数
#include
#include
unsigned long myStrlen(const char * str);
void myStrCpy(char *dest,const char* src);
int myStrCmp(const char *str1,const char* str2);
void myStrCat(char *str1,const char* str2);
int main(int argc, const char * argv[]) {
//strlen计算字符串的长度,不带最后的'\0';
//sizeof计算字符串所占内存的字节数,带最后的'\0';
char a[20] = "HELLO";
//原来将int a[20]数值数组进行传参的时候,需要传递的数组名与数组的大小;
//现在将字符数组传参时,只需要传递数组名即可。
printf("strlen = %lu\n",myStrlen(a));
printf("strlen = %lu sizeof = %lu\n",strlen(a),sizeof(a));
//strcpy
//strcpy(char * str1, const char * str2)
//作用:将字符串2的内容拷贝到字符串1中。同时会将str2后面的'\0'同时拷贝过去。
char b[20] = "we";
myStrCpy(a,b);
printf("%s\n",a);
printf("%c\n",a[3]);
//strcmp 字符串的比较 字符1与字符串2逐个字符比较,当出现不同的字符时,两个字符相减(ASCII相减)的结果就是字符串的比较结果。
printf("比较结果:%d %d\n",strcmp("ab", "abcd"),myStrCmp("ab","abcd"));
//字符串的拼接
char c[50] = "we are family!";
char d[20] = "we are friend!";
strcat(c, "we are friends!");
printf("%s\n",c);
myStrCat(c, d);
printf("%s \n",c);
//strchr在字符串中查找某个字符,返回值是该字符第一次出现的地址。
char * p = strchr("HELLO", 'L');
printf("%s\n",p);
//strstr在字符串中查找子串,返回子串第一次出现的地址
char *q = strstr("welcome to beijing!", "come");
printf("%s\n",q);
return 0;
}
unsigned long myStrlen(const char * str){
/*
int count = 0;
for (int i = 0; str[i]!='\0'; i++) {
count++;
}
return count;
*/
int i = 0;
for (; str[i]!='\0'; i++);
return i;
}
void myStrCpy(char *dest,const char* src){
int i = 0;
for (; src[i]!='\0'; i++) {
dest[i] = src[i];
}
dest[i] = '\0';
}
//"abcd\0" "abcd\0"
int myStrCmp(const char *str1,const char* str2){
int i = 0;
for (; str1[i]==str2[i]&&(str1[i]!='\0'||str2[i]!='\0'); i++);
int ret = str1[i]-str2[i];
return ret;
}
void myStrCat(char *str1,const char* str2){
int i=0,j=0;
for (;str1[i]!=0; i++);
for (; str2[j]!=0; j++) {
str1[i+j]=str2[j];
}
}
字符串的输入与输出函数
//要想使用字符串的函数 需要导入字符串的头文件
int main(int argc, const char * argv[]) {
char a[20];
printf("请输入一个字符串\n");
//使用scanf输入字符串时,字符串之间不允许输入空白字符(空格、tab、回车)
scanf("%s",a);
printf("%s\n",a);
/*
scanf 不允许输入空格
*/
//使用gets puts
// 输入一个字符串到内存中
gets(a);
//输出一个字符串
puts(a);
/*
gets 缺点:不会进行越界判断。
*/
//fgets fputs
//参数1:字符数组的地址
//参数2:输入的字节大小
//参数3:标准输入流
fgets(a, sizeof(a), stdin);
a[strlen(a)-1] = '\0';//消除最后读进来的换行符
//参数1:数组名
//参数2:标准输出流
fputs(a, stdout);
/*
fgets输入时会自动在最后一个字符上添加一个'\0',缺点:如果输入的字符串小于输入字节的大小,那么会将最后的'\n'读取进来。
*/
//"WWW"这个字符串 存储时要占4个字节。
//strlen函数计算字符串长度的时候不带后面的'\0';
printf("长度:%lu 占字节:%lu\n",strlen("WWW"),sizeof("WWW"));
// strlen("www");
isnumber(<#int#>)
return 0;
}
字符串相关的编外函数
int main(int argc, const char * argv[]) {
//将字符串转换成int数据,转换规则:从第一个非空白字符起,如果第一个非空白字符是数字或者正负号开始转换直到碰到第一个非数字字符停止转换。如果第一个非空白字符不是数字或不是正负号则转换失败,结果为0.
int a = atoi(" -12e3");
printf("%d\n",a);
//有关字符的方法 单引号括起来的单个字符
isnumber('2');
/*
int isalnum(int);//判断一个字符是否是数字字符或者是字母字符,是,返回1,不是返回0
int isalpha(int);//判断一个字符是否是字母
int isblank(int);
int iscntrl(int);
int isdigit(int);
int isgraph(int);
int islower(int);//判断是否是一个小写字母
int isprint(int);
int ispunct(int);
int isspace(int);
int isupper(int);//判断是否是大写字母
int isxdigit(int);
int tolower(int);//将大写转换成小写
int toupper(int);//将小写转成大写
int isascii(int);//判断是否是ascii码字符
int toascii(int);
*/
printf("%c\n",tolower('+'));
return 0;
}
字符数组-存储字符串
#include
int main(int argc, const char * argv[]) {
//声明一个字符数组
// char a[10];
//1、使用单个字符对其初始化 一般不用
//完全初始化
char a[5] = {'A','B','C','D','E'};
for (int i = 0; i < 5; i++) {
printf("%c ",a[i]);
}
printf("\n");
//部分初始化
char b[5] = {'A','B'};
//初始化为空
char c[5] = {};
//2、使用字符串对字符数组进行初始化。
//注意:一般字符数组的容量要定义的大一些
//
// char d[5] = "HELLOWWWWW";
// for (int i = 0; i < 20; i++) {
// printf("%c--%d ",d[i],d[i]);
// }
//%s是字符串的格式说明符 ,后面输出列表是字符数组的数组名
char d[10] = "wwwwwwww";
printf("%s\n",d);
d[0] = 'A';
printf("%s\n",d);
printf("\n%d\n",*(d+10));
//
return 0;
}
const修饰符
#include
int main(int argc, const char * argv[]) {
//const常量修饰符 被const修饰变量是只读变量
int const a = 10;
int b = 20;
int c = 33;
int * const p = &b;
//const放在*后面,修饰的是指针,即指针的指向不允许发生改变。
// p = &c;
// const int * p = &b; int const * p = &b;
//const放在*前面,修饰的是指针的指向。也就是说不能通过指针去修改所指变量的内容。
// *p = 100;
return 0;
}