指针与函数
设计一个函数,传入两个int参数,并交换这个两个参数的值
#include<stdio.h>
void swap(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
printf("a的值为:%d, b的值为:%d\n", a, b);
}
1int main(int argc, char const *argv[])
{
int m = 5;
int n = 10;
swap(m, n);
pritnf("m的值为:%d, n的值为:%d\n", m, n);
return 0;
}
这样录入的代码,会有两行结果:10 5和5 10


在main函数中执行swap语句,相当于是把m和n的值传到了另一个副本,而非是m和n这二者本身,在另外的副本中(函数swap)执行temp交换的代码部分之后,swap副本里面的值a和b就跟着发生了变化。
BUT!
这种改变并没有影响到原来的那个m和n
简单通俗地说,发生变化的只有他们的值,不是他们的地址
这样的函数,能够让两个变量的值发生转换......
可是,如何做到让m和n这两个参数变量的值直接转换,而不是通过不相关的变量实现呢?
#include<stdio.h>
swap(int*a, int* b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
printf("a=%d,b=%d\n",*a, *b);
return 0;
}
int main(int argc, char const *argv[])
{
int m = 5;
int n = 10;
swap(&m, &n);
pirntf("a=%d,b=%d\n",m, n);
return 0;
}
只需要加上地址就可以了。

如果m和n的地址分别是在101和105上,那么当在main函数中用&(取地址符)将二者的地址传入swap函数当中,那么此刻swap函数中的m和n不再是m和n的值,而是它们的地址里存放的值。

在swap函数中temp交换代码部分执行过后,a和b的值确实会互换,但互换的是m和n的地址里存放的值。
输出结果会是 10 5和 10 5
指针与数组
在C语言中,指针与数组的关系十分密切。
通过数组下标能完成的操作都可以通过指针完成。
一般来说,用指针编写的程序比用数组下标编写的程序执行速度快。
int a[] = {15, 22, 67, 43, 87};
int *p;
p = a;
printf("%p\n", a);
printf("%p\n", p);
printf("%d\n", *p);
像上述代码,如果把a直接赋值给p,数组的首地址会给赋值给p,*p是根据地址找到值,那么*p会输出什么?
据我们所知,a数组中的首元素地址会被赋值给p,那么*p就会根据首元素地址找到首元素这个值,理所应当地输出15
那么按照这个定理,如果我想要输出的不是15,我想要输出后面的22,67,43,87这些数,是不是意味着如果我将a数组中别的地址赋值给p,*p就会输出该地址所含的值呢?
int a[] = {15,22,67,43,87};
int *p;
p = a;
for(int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
{
printf("%d\n"; a[i]);
}
for(int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
{
printf("%d\n", *(p + i));
}

声明一个数组,并将数组首地址赋值给*p;
然后经历一次数组的遍历循环;
用指针遍历一下数组,*p存储的是首地址的值,那么让*p也遍历不就行了。
*(p + i) ......i++
这个时候,*p就会跟着i遍历数组而输出当前i所代表下标的值。
不过此时,问题又来了。*(p+i)能跟着i遍历而输出遍历的值,可是括号内的p此时代表的是地址,地址101+(假设i=1)1=102,那么他应该输出遍历的值应该是(101 102 103 104 105)的地址,又怎么能做到准确无误地输出(101 105 109 113 117 121)所下标的数组的值呢?
在对指针做算术运算的时候,规则和我们平常的规则有些不一样,由此引出我们下一个概念------
指针做算术运算
给指针加上一个整数,实际上加的是这个整数和指针数据类型对应字节数的乘积。
int a = 5;
int *p = &a;
printf("%p\n", p);
p++;
printf("%p\n", p);
return 0;
p++ --> p = p + 1 --> p = p + 1 * 4
可以理解为 p + 1其实是 p + 1*sizeof(int)
对指针来说,指针+1其实代表的是指针走了一个步长。
(注意,p++要单独拿出来,因为*p++和*(p+1)不是一个意思。写作*p++,表示的是先用*访问p++的返回值也就是p,再p=p+1.)

结构体的声明
结构体是一个或多个变量的集合,这些变量可以是不同的类型。
本质上,结构体其实就是用户(你)定义的数据类型,只不过这种数据类型是自定义的。
可是,我们日常代码作业中能够遇到那么多的数据类型,char\short\long\int\double......等,难道这些数据类型不够用吗?
答案是确实不够用,假设我们现在需要声明二维xy轴上的坐标点,普通的数据类型很难帮我表示出来这些,即便能够表示,那也是需要两个变量,定义个一个int数据类型声明x,在定义一个int数据类型声明y......
这样确实能够帮我们声明一个函数轴上的xy坐标点,可是如果我只想用一个变量就解决xy坐标点,这个时候结构体就是一个很好的选择。
struct 结构体名
{
数据类型 变量名;
数据类型 变量名;
......
};
实现xy坐标轴的结构体
struct point
{
int x;
int y;
};
结构体的初始化与调用
struct 结构体名 变量名;
struct point p;
给结构体中的xy赋值规矩如下:
变量名.结构体内部变量名 = 值
p.x = 10;
p.y = 15
#include<stdio.h>
struct point
{
int x;
int y;
}
int main(int argc, char const *argv[]){
struct point p;
p.x = 5;
p.y = 10;
printf("%d\n", p.x);
printf("%d\n", p.y;
return 0;
}
结构体实例--创建一个point
编写一个函数,传入两个int参数,在函数创建一个结构体point类型的变量,将传入的两个参数分别赋值给该结构体变量的x和y,最后将该结构体变量返回 。
struct point createPoint(int x, int y)
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}
#include<stdio.h>
struct point{
int x;
int y;
};
struct point creatPoint(int x, int y){
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}
int main(int argc, char const *argv[])
{
struct point p;
p = creatPoint(5, 10);
printf("%d\n", p.x);
printf("%d\n", p.y);
return 0;
}
结构体与指针
在一些场景中,如果传递给函数的结构体很大,使用指针方式的效率通常更高。
pp指向一个point结构体
struct point *pp;
为pp所指向的结构体中的属性赋值
(*pp).x = 10;
(*pp).y = 5;
为了方便使用,C语言提供了另外一种简写方式
pp->x = 10;
pp->y = 5;
大概是这种书写形式:结构体指针->结构体成员名
#include<stdio.h>
struct point
{
int x;
int y;
};
int main(int argc, char const *argv[])
{
struct point p;
p.x = 5;
p.y = 10;
struct point *pp;
pp = &p;
pp->x = 10;
pp->y = 5;
printf("x=%d,y=%d\n", p.x, p.y);
printf("x=%d,y=%d\n", pp->x, pp->y);
return 0;
}
用指针通过地址去改原本struct point p里面的值
类型定义
typedef 数据类型 别名
typedef int zx
#include<stdio.h>
typedef int myType1;
typedef char myType2;
int main(int argc, char const *argv[])
{
myType1 a = 5;
myType2 b = '0';
printf("%d\n", a);
printf("%c\n", b);
return 0;
}
typedef也经常用在结构体上:
每次声明结构体变量都要写struct关键字,很麻烦,而且逻辑上也很难受,typedef可以解决这个问题。
struct point
{
int x;
int y;
}
int main(int argc, char const *argv[])
{
struct point p;
p.x = 5;
p.y = 10;
printf("%d\n", p.x):
printf("%d\n", p.y);
return 0;
}
而利用typedef解决有两种解法:
解法1:
typedef struct 结构体名
{
数据类型 变量名;
数据类型 变量名;
......
}别名;
解法2:
typedef struct
{
数据类型 变量名;
数据类型 变量名;
......
}别名;
实际的运用
typedef struct point
{
int x;
int y;
}po;
typedef struct
{
int x;
int y;
}po;
更多更常用的第二种,typedef+结构名
#include<stdio.h>
typedef struct
{
int x;
int y;
}po;
int main(int argc, char const *argv[])
{
po p;
p.x = 5;
p.y = 10;
printf("%d\n", p.x);
printf("%d\n", p.y);
return 0;
}
内存的分类
C程序编译后,会以三种形式使用内存:
**静态/全局内存:**静态声明的变量和全局变量使用这部分内存,这些变量在程序开始时分配,直到程序终才消失。
**自动内存(栈内存):**函数内部声明的变量使用这部分内存,在函数被调用时才创建。
通常我们在函数里面写的东西,肯定不算是全局变量,也没有加类似static关键字的一定不叫静态。我们目前为止写的日常的代码,基本都保存在栈中。
我们平常在函数中写的代码(main函数又或者自己创建的函数),都是在函数中的代码基本上都是在栈内存当中的
栈内存:我们每一次写代码,它会自动在栈内存当中创建一片空间,存储这个东西。自动也意味着我们不能控制内存,栈内存的自动存储是不受控制的,我们没有办法决定去说它什么时候开辟空间,什么时候释放空间,还可能遇到我们写的代码在我们不知情下,把这片空间释放了。
**动态内存(堆内存):**根据需求编写代码动态分配内存,可以编写代码释放,内存中的内容直到释放才消失。
堆内存,就是受我们控制的内存空间。
动态内存分配:
在C语言中,动态分配内存的基本步骤:
1:使用malloc (memory allocate)函数分配内存
void* malloc(size_t)
用这个函数,就是要从堆内存中开辟空间,但是需要开辟多大的空间,堆内存不知道,所以需要传入这个参数,告诉系统要开辟多大的空间,再把地址指针返回。
比喻的话:就是让一个人去创建多大的房子,告诉他需要三室两厅还是两室一厅,要平房还是别墅,在房子盖好后,还需要从那个人口中知道房子在哪儿,房子的地址......
还有一个问题,为什么要用void*?
因为void是通用函数类型,如果往这片空间存入一个int类型的数据变量,那么返回值就是int*的指针;如果存入一个point结构体变量,那么返回值就是point*的结构体指针,诸如此类。
在一开始,系统并不不知道我们要往内存空间存储什么东西,它只负责给我返回指针,而得到返回指针的我们,清楚我们需要的是什么类型的数据类型,那么便可以强制转换,把void*转换成我们想要的(int* double* point*诸如此类)(但是要考虑字节的问题)
如果成功,会返回从堆内存上分配的内存指针
如果失败,会返回空指针
2:使用分配的内存
3:使用free函数释放内存
动态内存分配实例 - 基本数据类型
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char const *argv[])
{
int *p;
p = (int*)malloc(sizeof(int));
*p = 15;
printf("%d\n", *p);
free(p);
return 0;
}
动态内存分配示例 - 字符串
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char const *argv[]){
char *s;
s = (char*)malloc(10);
strcpy(s, "Hello");
printf("%s\n", s);
return 0;
}
动态内存分配示例 - 数组
#include<stdio.h>
#inlcude<stdlib.h>
int mian(int argc, char const *argv[]){
int *arr = (int*)malloc(5 * sizeof(int));
for(int i = 0; i < 5; i++)
{
arr[i] = 0;
}
for(int i = 0; i < 5; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
动态内存分配示例 - 结构体
#include<stdio.h>
#include<stdlib.h>
typedf struct
{
int x;
int y;
}po;
int main(int argc, char const *argv[]){
po *p;
po = (po*)malloc(sizeof(po));
p.x = 5;
p.y = 10;
printf("x = %d, y = %d\n", p.x, p.y);
return 0;
}
值得注意的是,结构体变量占位字节具有补位机制