一、指针
1.指针的重要性
指针是C语言中非常重要的数据类型,如果你说C语言中除了指针,其他你都学得很好,那你干脆说没学过C语言。
2.小需求
- void change(int n)函数调用完毕后,改变实参的值
- 分析:修改实参的值->找到存储空间->地址
二、指针变量的定义
1.定义的格式
- 类名标识符 *指针变量名;
int *p;
2.先定义后赋值
- 简单取值
int a = 10;
int *p;
p = &a;
printf(“%d”, *p);
- 简单改值
*p = 9;
3.定义的同时赋值
int a = 10;
int *p = &a;
4.实现修改实参
5.注意点
- int *p; p = 1000;
- int *p; *p = 100;
- int *p; *p = &a;
- %p输出指针里面存储的地址值
- 其他指针类型说明,比如float *p; char *p;
- 不能乱用类型,比如int a = 10; float *p = &a;
6.清空指针
p = 0;
p = NULL;
三、指针实例
1.void swap(char *a, char *b) (注意temp=a; a = b; b = temp;)
2.int sumAndMinus(int a, int b, int *minus)
四、指针探究
1.指针变量所占用的存储空间
2.为何指针变量要分类型?
int i = 2;
char c = 1;
int *p = &c;
printf(“%d”, *p);
五、指针与数组
1.指向一维数组元素的指针
2.用指针遍历一维数组元素
- 先遍历char数组(‘i’,’t’),再遍历int类型数组
- (p+i)和(p++)的区别
- a+i和a++
- p[0]、p[1]
3.习题
1>设计一个函数:int arraySum(int a[], int n),求一维数组a前n个数的和。现在利用int *p替换int a[]
六、指针与字符串
1.字符串回顾
char s[] = “hello”;
2.其他定义字符串的方式
char *s = “hello”;
或者
char *s;
s = “hello”;
3.两种定义方式的区别
- 内存分析
- 画图分析
- 常量和变量字符串的区别
- 常量的内存地址查看
4.习题
1>编写一个int string_len(char *s),返回字符串s的字符长度
七、返回指针的函数
- 指针也是C语言中的一种数据类型,因此一个函数的返回值肯定可以是指针类型的
- 返回指针的函数的一般形式为:类型名 * 函数名(参数列表)
八、指向函数的指针
1.为什么指针可以指向一个函数?
函数作为一段程序,在内存中也要占据部分存储空间,它也有一个起始地址,即函数的入口地址。函数有自己的地址,那就好办了,我们的指针变量就是用来存储地址的。因此,可以利用一个指针指向一个函数。其中,函数名就代表着函数的地址。
2.指向函数的指针的定义
定义的一般形式:函数的返回值类型 (*指针变量名)(形参1, 形参2, ...);
3.使用注意
- 由于这类指针变量存储的是一个函数的入口地址,所以对它们作加减运算(比如p++)是无意义的
- 指向函数的指针变量主要有两个用途:
- 调用函数
- 将函数作为参数在函数间传递
九、指针总结
一、指针变量的定义
1. 格式:变量类型 *指针变量名;
2. 举例:int *p; char *p2;
3. 注意:定义变量时的*仅仅是指针变量的象征
二、利用指针变量简单修改其他变量的值
1.指向某个变量
int a;
int *p;
p = &a;
或者
int *p = &a;
2.修改所指向变量的值
*p = 10;
3.在函数内部修改外面变量的值
int a = 10;
change(&a);
void change(int *n)
{
*n = 20;
}
三、指针与数组
1.将数组当做函数参数传入时,会自动转为指针
四、指针与字符串
1.定义字符串的2种方式
1> 利用数组
char name[] = "zhang san";
* 特点:字符串里面的字符是可以修改的
* 使用场合:字符串的内容需要经常修改
2> 利用指针
char *name = "zhang san";
* 特点:字符串其实是一个常量字符串,里面的字符是不能修改
* 使用场合:字符串的内容不需要修改,而且这个字符串经常使用
2.定义字符串数组
1> 利用二维字符数组
char names[2][10] = {"jack", "rose"};
2> 利用指针数组
char *names[2] = {"jack", "rose"};
十、程序实现
01-指针
void change_value(int *n);
void pointer(void) {
// 变量类型 变量名;
// 格式:变量类型 *变量名;
// 定义了一个指针变量p
// 指针变量只能存储地址
// 指针就一个作用:能够根据一个地址值,访问对应的存储空间
// 指针变量p前面的int:指针变量p只能指向int类型的数据
int *p;
int a = 90;
// 指针变量p指向了变量a
p = &a;
*p = 10;
a = 20;
printf("*p = %d\n", *p);
//printf("a = %d\n", a);
/*
int a1 = 90;
change_value(&a1);
printf("a1 = %d\n", a1);
*/
}
void change_value(int *n) {
*n = 10;
}
02-指针的使用注意
void use_pointers_note(void) {
/* 不建议的写法, int *p只能指向int类型的数据
int *p;
double d = 10.0;
p = &d;
*/
/* 指针变量只能存储地址
int *p;
p = 200;
*/
/* 指针变量未经过初始化,不要拿来间接访问其他存储空间
int *p;
printf("%d\n", *p);
*/
int a = 10;
/*
int a;
a = 10;
*/
/*
int *p;
p = &a;
*/
// 定义变量时的*仅仅是一个象征,没有其他特殊含义
int *p = &a;
// 不正确的写法
// *p = &a;
p = &a;
// 这个时候的*的作用:访问指向变量p指向的存储空间
*p = 20;
char c = 'A';
char *cp = &c;
*cp = 'D';
printf("%c\n", c);
}
03-指向指针的指针
void pointer_to_pointer(void) {
int a = 10;
int *p = &a;
int **pp = &p;
// a = 20;
// *p = 20;
/*
(*pp) == p
*(*pp) == *p = a
**pp == *p = a
*/
**pp = 20;
printf("%d\n", a);
//int ***ppp = &pp;
/*
char a2 = 'A';
char *p2 = &a2;
*/
}
04-指针
void pointer_practice(void) {
double d1 = 10.51;
double d2 = 10.52;
double *p;
p = &d1;
*p = 10.91;
p = &d2;
*p = 10.99;
p = 0; // 清空指针
// p = NULL; // 清空指针
// 清空指针后,不能再间接访问其它存储空间
// *p = 100.99; // Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
printf("d1 = %f, d2 = %f\n", d1, d2);
}
05-指针的使用
void user_pointer(void) {
char a = 'A';
char *c = &a;
char **cc = &c;
char ***ccc = &cc;
a = 'G';
printf("a = %c, %p\n", a, &a);
printf("*c = %c, %p\n", *c, &c);
printf("**cc = %c, %p\n", **cc, &cc);
printf("***c = %c, %p\n", ***ccc, &ccc);
}
06-指针练习1
void swap(int *v1, int *v2);
void pointer_practice_1(void) {
int a2 = 90;
int b2 = 89;
swap(&a2, &b2);
printf("a2=%d, b2=%d\n", a2, b2);
}
/*
// 交换的只是内部v1、v2的值
void swap(int v1, int v2) {
int temp = v1;
v1 = v2;
v2 = temp;
}
// 不能交换外面实参的值,仅仅是交换了内部指针的指向
void swap(int *v1, int *v2) {
int *temp;
temp = v1;
v1 = v2;
v2 = temp;
}
*/
// 完成两个整型变量值的互换
void swap(int *v1, int *v2) {
int temp = *v1;
*v1 = *v2;
*v2 = temp;
}
07-指针练习2
int sumAndMinus(int n1, int n2, int *n3);
void pointer_practice_2(void) {
int a = 10;
int b = 7;
// 存储和
int he;
// 存储差
int cha;
he = sumAndMinus(a, b, &cha);
printf("和是:%d, 差是:%d\n", he, cha);
}
// 返回值是
int sumAndMinus(int n1, int n2, int *n3) {
*n3 = n1 - n2;
return n1 + n2;
}
08-任何指针都占用8个字节的存储空间
void any_pointer_takes_up_8_bytes_of_storage_space(void) {
/*
%d int
%f float\double
%ld long
%lld long long
%c char
%s 字符串
%zd unsigned long
*/
/*
0000 0001
0000 0010
0000 0000
0000 0000
0000 0000 0000 0000 0000 0010 0000 0001
*/
/*
// 0000 0000 0000 0000 0000 0000 0000 0010
int i = 2;
// 0000 0001
char ch = 1;
char *p;
p = &ch;
// *p = 10;
printf("ch的值是%d\n", *p);
*/
char c = 'a'; // 1
int a = 22; // 4
long b = 2333232343545545455; // 8
// 任何指针都占用8个字节的存储空间
char *cp = &c;
int *ap = &a;
long *bp = &b;
printf("cp = %zd, ap = %zd, bp = %zd\n", sizeof(cp), sizeof(ap), sizeof(bp));
*cp = 'K';
*ap = 999;
*bp = 8888888888888888888;
printf("c = %c, a = %d, b = %ld\n", c, a, b);
}
09-指针和数组
void change_array(int array[]);
void change_array2(int *array);
void pointers_and_arrays(void) {
/*
1.数组元素的访问方式
int ages[5];
int *p;
p = ages;
1> 数组名[下标] ages[i]
2> 指针变量名[下标] p[i]
3> *(p + i)
2.指针变量+1,地址值究竟加多少,取决于指针的类型
int * 4
char * 1
double * 8
*/
/*
// 20个字节
int ages[5] = {10, 11, 19, 78, 67};
change_array2(ages);
*/
double d = 10.81;
double *dp;
dp = &d;
printf("dp = %p\n", dp);
printf("dp + 1 = %p\n", dp + 1);
int ages[5] = {22,31,33,24,23};
int *p;
// 指针变量p指向了数组的首元素
p = &ages[0];
// 数组名就是数组的地址,也是数组首元素的地址
//p = ages;
*p = 99;
p[2] = 100;
/*
p ---> &ages[0];
p + 1 ---> &ages[1];
p + 2 ---> &ages[2];
p + i ---> &ages[i];
*/
printf("*(p+3) = %d\n", *(p+3));
printf("p[3] = %d\n", p[3]);
for (int i = 0; i < 5; i++) {
printf("ages[%d] = %d\n", i, *(p + i));
}
printf("p ---> %p\n", p);
printf("p + 1 ---> %p\n", p + 1);
printf("p + 2 ---> %p\n", p + 2);
}
/*
void change_array(int array[]) {
int s = sizeof(array);
printf("%d\n", s);
}
// 利用一个指针来接收一个数组,指针变量array指向了数组的首元素
void change_array2(int *array) {
printf("%d\n", array[2]);
printf("%d\n", *(array+2));
}
*/
10-数组元素的访问方式
void accessed_array_elements(void) {
// 1、数组元素的访问方式
int ages[5] = {22, 31, 44, 56, 23};
int *p;
p = ages;
for (int i = 0; i < 5; i++) {
int a = ages[i]; // 1.数组名[下标] ages[i]
int b = p[i]; // 2.指针变量名[下标] p[i]
int c = *(p + i); // 3.*(p + i)
printf("a[%d] = %d, b[%d] = %d, c[%d] = %d\n", i, a, i, b, i, c);
}
// 2、指针变量+1,地址究竟加多少,取决于指针的类型
// int * 4
// char * 1
// double * 8
}
11-指针和字符串
void pointers_and_strings(void) {
/*
1.常量区
存放一些常量字符串
2.堆
对象
3.栈
存放局部变量
定义字符串的2种方式
1> 利用数组
char name[] = "zhang san";
* 特点:字符串里面的字符是可以修改的
* 使用场合:字符串的内容需要经常修改
2> 利用指针
char *name = "zhang san";
* 特点:字符串其实是一个常量字符串,里面的字符是不能修改
* 使用场合:字符串的内容不需要修改,而且这个字符串经常使用
*/
/*
char name[20];
printf("请输入姓名:\n");
scanf("%s", name);
// 'j' 'a' 'c' 'k' '\0'
// printf("%c\n", name[3]);
// printf("刚才输入的字符串是:%s\n", name);
*/
/*
// 定义字符串数组
char *name = "jack";
//int ages[5];
// 指针数组(字符串数组)
char *names[5] = {"jack", "rose", "jake"};
// 二维字符数组(字符串数组)
char names2[2][10] = {"jack", "rose"};
*/
// 定义字符串
// 字符串变量
char name[] = "hel";
name[0] = 'T';
printf("%s\n", name);
printf("%lu\n", sizeof(name));
// "hel" == 'h' + 'e' + 'l' + '\0'
// 指针变量name2指向了字符串的首字符
// 字符串常量
char name_1[] = "ad_1";
char name_2[] = "ad_1";
printf("%p\n%p\n", name_1,name_2);
printf("%s\n%s\n", name_1,name_2);
char *name1 = "ad_2";
char *name2 = "ad_2";
// *name1 = 'T'; // Thread 1: EXC_BAD_ACCESS (code=2, address=0x100000fae)
printf("%p\n%p\n", name1,name2);
printf("%s\n%s\n", name1,name2);
}
12-指针和字符串练习
long string_length(char *s);
void pointers_and_strings_practice(void) {
/*
(不包括\0)
编写一个int string_len(char *s),返回字符串s的字符长度
*/
long size = string_length("hello");
printf("%ld\n", size);
}
13-字符串长度
/*
long string_length(char *s) {
// 记录字符的个数
int count = 0;
// 如果指针当前指向的字符不是'\0'
while ( *s != '\0') {
// 个数+1
count++;
// 让指针指向下一个字符
//s = s + 1;
s++;
}
return count;
}
long string_length(char *s) {
// 记录字符的个数
int count = 0;
// 如果指针当前指向的字符不是'\0'
// 首先*s取出指向的字符
// 然后s++
while ( *s++ ) {
// 个数+1
count++;
// 让指针指向下一个字符
//s = s + 1;
//s++;
}
return count;
}
*/
long string_length(char *s) {
// 1.定义一个新的指针变量指向首字符
char *p = s;
/*
while ( *s != '\0' )
{
s++;
}*/
while ( *s++ ) ;
return s - p - 1;
}
14-判断某个字符串是否为回文
// 回文就是从左边开始读 和 从右边开始读 都是一样的,比如"abcba"
/*
返回1代表是回文
返回0代表不是回文
*/
int determines_string_is_palindrome(char *str) {
// 1.定义一个指向变量left指向字符串的首字符
char *left = str;
// 2.定义一个指向变量right指向字符串的末字符
char *right = str + string_length(str) - 1;
while (left < right) {
// // 如果左边和右边的字符不一样
if (*left++ != *right--) {
return 0;
}
}
return 1;
}
15-将两个字符串拼接在一起
void string_link1(char s[], char t[]) {
int i = 0;
// 判断s[i]是否为字符串的尾部
while (s[i] != '\0') {
i++;
}
int j = 0;
// 拷贝t的内容到s的后面
while ((s[i] = t[j]) != '\0') {
i++;
j++;
}
/*
更加精简的写法,仅作为参考(会有警告信息)
void strlink2(char s[], char t[]) {
int i = -1;
// 判断s[i]是否为字符串的尾部
while (s[++i]) ;
int j = 0;
// 拷贝t的内容到s的后面
while (s[i++] = t[j++]) ;
}
*/
}
16-将两个字符串拼接在一起
void string_link2(char *s, char *t) {
// 判断s[i]是否为字符串的尾部
while (*s != '\0') {
s++;
}
// 拷贝t的内容到s的后面
while ((*s = *t) != '\0') {
s++;
t++;
}
/*
更加精简的写法,仅作为参考(会有警告信息)
void strlink2(char *s, char *t) {
// 判断s[i]是否为字符串的尾部
while (*s++);
// 减回一次
s--;
// 拷贝t的内容到s的后面
while ( *s++ = *t++ ) ;
}
*/
}
17-返回指针的函数
char *returns_function(void);
void returns_pointer_function(void) {
char *name = returns_function();
printf("name = %s\n", name);
}
char *returns_function(void) {
return "zhang san";
}
18-指向函数的指针
double haha(double d, char *s, int a) {
return 0;
}
void test() {
printf("调用了test函数\n");
}
int pointer_sum(int a, int b) {
return a + b;
}
void pointer_to_function(void) {
/*
1.看懂语法
2.定义指向函数的指针
double (*p)(double, char *, int);
p = haha;
或者
double (*p)(double, char *, int) = haha;
3.如何间接调用函数
1> p(10.7, "jack", 10);
2> (*p)(10.7, "jack", 10);
*/
// 定义指针变量指向sum函数
// 左边的int:指针变量p指向的函数返回int类型的数据
// 右边的(int, int):指针变量p指向的函数有2个int类型的形参
int (*p)(int, int);
p = pointer_sum;
//int c = p(10, 11);
//int c = (*p)(10, 11);
int c = pointer_sum(10, 9);
printf("c is %d\n", c);
}
void test1() {
// (*p)是固定写法,代表指针变量p将来肯定是指向函数
// 左边的void:指针变量p指向的函数没有返回值
// 右边的():指针变量p指向的函数没有形参
void (*p)();
// 指针变量p指向了test函数
p = test;
p();
//(*p)(); // 利用指针变量间接调用函数
//test(); // 直接调用函数
}
网友评论