1. 数组与指针
概述:数组由若干个类型相同的元素组成,在内存中占据一段连续的存储空间。数组名就是其存储空间的起始地址,本身就是一个指针。
1.1 指针访问数组元素
代码演示:
#include<stdio.h>
int main()
{
int a[5] = { 1,2,3,4,5 };
int *p=a; //等价于p=&a[0] ,把数组的首地址给指针p
//以下的方式,基于数组内的元素地址连续。
printf("----------指针法---------\n");
for (int i = 0; i < 5; ++i)
{
printf("*(p+%d) = %d a[%d]的首地址为:%p\n", i, *(p + i), i, p + i);
}
printf("----------地址法---------\n");
for (int i = 0; i < 5; ++i)
{
printf("*(a+%d) = %d a[%d]的首地址为:%p\n", i, *(a + i), i, a + i);
}
printf("----------下标法---------\n");
for (int i = 0; i < 5; ++i)
{
printf("a[%d] = %d a[%d]的首地址为:%p\n", i, a[i], i, &a[i]);
}
printf("----------下标法---------\n");
printf("这里只是把a换成p,因为p和a值相同,所以与上面的下标法等价\n");
for (int i = 0; i < 5; ++i)
{
printf("a[%d] = %d a[%d]的首地址为:%p\n", i, p[i], i, &p[i]);
}
return 0;
}
运行结果:
注:
①下标法和地址法访问效率相同,下标法直观,易懂。指针法运行速度快。
②a是可以看成是指针常量,p是指针变量,故p++正确,a++错误,因为a是常量,不能改变。
1.2 指向多维数组的指针
数组的定义(抽象数据类型)数据结构里的数组较为复杂,其实不知道也没有关系,这里只要知道多维数组怎样存储在内存中的就行了,C语言里面我们一般讨论的是一维到三维的数组。这里我们一二维数组为例。
简单说一下二维数组如何存储在内存里的。我们都知道一维数组的存储是连续的,同样的,二维数组的存储也是连续的,地址是这样连续的:
如有二维数组a[3][3]
a[0][0] //先是第一个元素的二维数组的地址,等到第一
a[0][1] //个元素的二维数组地址完,才到第二个元素的地址
a[0][2] //依次类推
a[1][0]
… … … …
代码演示:
#include<stdio.h>
int main()
{
int a[3][4] = {
{1,2,3,4},
{11,12,13,14},
{21,22,23,24}
}; //初始化
int *p = &a[0][0]; //等价于p=a[0]
for (p = &a[0][0]; p < &a[2][3]; ++p)
{
printf("%d ", *p);
}
//运行结果:1 2 3 4 11 12 13 14 21 22 23
return 0;
}
注意:不能写成 p = a;如果这么写了,那么*p = a[0] ,而a[0]在二维数组中是a[0][0]的地址,而不是值,故会出错。
2. 字符串和指针
2.1 指针处理字符串
C语言中没有字符串类型,C语言以字符数组充当字符串。
代码演示:(以实现函数strcat功能为例)
#include<stdio.h>
void my_strsta(char *str1, char *str2)
{
while (*str1)
str1++;
//先取值在自增,指针指向最后的'\0'自然会停下来
while (*str1++ = *str2++);//这里有个分号
}
int main()
{
char str1[15] = "hello ";
char str2[10] = "world!";
char *p1, *p2;
p1 = str1;
p2 = str2;
printf("str1 = %s\n", str1);
printf("str2 = %s\n", str2);
printf("-------调用函数后-------\n");
my_strsta(str1, str2);
printf("str1 = %s\n", str1);
printf("str1 = %s\n", str2);
return 0;
}
运行结果:
2.2使用字符指针变量和字符数组的区别
代码演示:
#include<stdio.h>
int main()
{
char str[] = "hello world";
char *p = "hello C";
printf("str = %s\n", str);
printf("p = %s\n", str);
str[0] = 'W'; //可以单个赋值
//p[0] = 'W'; //不可以单个赋值
printf("修改后---\n");
printf("str = %s\n", str);//str = "hhhhhhh" //只能定义的时候初始化,不能赋值
p = "hhjhhhhh"; //可以再次赋值
printf("p = %s", p);
return 0;
}
运行结果:
3. 函数和指针
3.1 普通变量作函数参数
代码演示:(经典例题用函数交换两数)
#include<stdio.h>
void swap_1(int a, int b)
{
int t;
t = a;
a = b;
b = t;
}
void swap_2(int *a, int *b)
{
int t;
t = *a;
*a = *b;
*b = t;
}
int main()
{
int a = 12, b = 15;
int x = 19, y = 99;
printf("交换后\n");
swap_1(a, b);
printf("a = %d,b = %d\n", a, b); //将会失败
swap_2(&x, &y);
printf("x = %d,y = %d\n", x, y); //将会成功
return 0;
}
3.2 数组名作函数的参数
代码演示1:(冒泡排序)
#include<stdio.h>
void bubbleSort(int a[], int n) //非降序
{
int i, j;
int t;
for (i = 0; i < n - 1; i++) //n个数,花n-1次来确定大小关系
{
for (j = 0; j < n - 1 - i; j++) //确定一个数的大小得分别与其他数比较
{ //每次比较确定一个数,下次便可以少比较一个数
if (a[j + 1] < a[j])
{
t = a[j + 1];
a[j + 1] = a[j];
a[j] = t;
}
}
}
}
int main()
{
int a[10] = { 12,2,33,-4,45,-6,37,84,59 };
for (int i = 0; i < 10; i++)
{
printf("%3d", a[i]);
}
printf("\n排序后----------------\n");
bubbleSort(a,10);
for (int i = 0; i < 10; i++)
{
printf("%3d", a[i]);
}
return 0;
}
运行结果:
代码演示2:(选择排序 )
#include<stdio.h>
#include<stdlib.h>
void printArray(int a[][6], int n)
{
for (int i=0; i < n; ++i)
{
for (int j=0; j < 6; j++)
{
printf("%3d", a[i][j]);
}
printf("\n");
}
}
void insertSort(int a[][6], int n) //6必须加,n为第一维得长度
{ //非降序
int i, j;
int min;
for (i = 0; i < n * 6; i++)
{
min = i;
for (j = i + 1; j < n * 6 - 1; j++)
{
if (a[j / 6][j % 6] < a[min/6][min%6])
min = j;
}
if (min != i)
{
int t = a[min / 6][min % 6];
a[min / 6][min % 6] = a[i / 6][i % 6];
a[i / 6][i % 6] = t;
}
}
}
int main()
{
int a[5][6] = { 0 };
for (int i=0; i < 5; i++)
{
for (int j=0; j < 6; j++)
{
a[i][j] = rand() % 100;//函数rand生成伪随机数,每次运行得随机数都一样
} //rand在头文件<stdlib.h>中
}
printf("排序前:\n");
printArray(a, 5); //打印二维数组
printf("排序后:\n");
insertSort(a, 5);
printArray(a, 5);
return 0;
}
运行结果:
3.3 指针作为函数得返回值
代码演示:
#include<stdio.h>
int *fun(int a[],int n)
{
for (int i = 0; i < n; ++i)
{
a[i] = i+1;
}
return a;
}
int main()
{//例子除了简单之外,没啥用
int a[10];
int *p = fun(a,10);
for (int i = 0; i < 10; i++)
{
printf("%3d", *(p + i));
}
return 0;
}
3.3 指向函数的指针
声明格式:
数据类型 (*指针变量名)(形参列表)
代码演示:
#include<stdio.h>
int add(int a, int b)
{
return a + b;
}
int minus(int a, int b)
{
return a - b;
}
// int(*fun)(int x, int y);
// x,y为占位符有没有都行
// int为数据类型,即返回值类型
// fun为指针变量名
// int x,int y为形参列表
int compute(int a, int b, int(*fun)(int, int))
{ //最后一个参数是函数参数
return (*fun)(a, b);
}
int main()
{
int a = 12, b = 15;
printf("%d + %d = %d\n", a, b, compute(a, b, add));
printf("%d - %d = %d\n", a, b, compute(a, b, minus));
return 0;
}
运行结果:
小结:那么指针函数究竟有什么用呢?
①它可以管理,同类型的函数。
②多态
③当然,还用很多用处
4. 指针数组
4.1 指针数组的定义
指针数组中的每一个元素都是一个指针,并且指向相同类型的变量。
定义格式:
类型名 *数组名[数组长度]
代码演示:
#include<stdio.h>
int main()
{
char *p[5] = {
"one",
"two",
"three",
"four",
"five"
};
for (int i = 0; i < 5; i++)
{
printf("%s\n", p[i]);
}
return 0;
}
//运行结果:
one
two
three
four
five
注:数组中的每个元素只存储了字符串的首地址,故只能访问字符串,不能修改字符串中的字符。不然会有类似的错误。
4.2 带参数的main函数
用于接收程序运行过程中,用户输入的参数
定义格式:
void main(int argc,char *argv[])
{
…
}
注:若main函数带参数,只能带这两中类型的参数,否则就不带。
代码演示:
#include<stdio.h>
int main(int argc,char *argv[])
{
for (int i = 0; i < argc; ++i)
{
printf("%s\n", argv[i]);
}
return 0;
}
使用步骤:
①运行上述代码,生成一个可执行文件
②按win+R 输入cmd打开命令行(windows系统)
③进入保存可执行文件的路径
④输入可执行文件的名字即可执行
⑤若在可执行文件的后面进行输入,那么输入将会被那俩参数收到。
如图:
小结:argc=参数个数+1
argv[0]存储文件名的首地址
argv[1]存储第一个参数的首地址
argv[2]存储第二个参数的首地址
argv[3]存储第三个参数的首地址
5. 数组指针
注:二维数组int a[m][n],可以看成是长度为m类型为int[n]的一维数组
声明格式:
数据类型 (*变量名)[一维数组的长度]
代码演示:
#include<stdio.h>
int main()
{
int(*p)[4]; //声明指针,不过指针没有指向
int a[3][4] = { //初始化
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
p = a; //p指向数组a中的int[4]类型,等价于 p=&a[0]
//故*p = a[0],又*p=a[0]等价于*p=&a[0][0]
//因此**p = a[0][0],依次类推
for (int i=0; i < 3; ++i)
{
printf("**(p+%d) = %d\n", i, **(p + i));
//p指向int[4]类型,故每次p+i,实际上是偏移了1*4*sizeof(int)=16
printf("p+%d的地址为: %p\n",i, p + i);
}
/*
运行结果: (十六进制)
**(p + 0) = 1
p + 1的地址为: 004FFA18
**(p + 1) = 5
p + 1的地址为: 004FFA28
**(p + 2) = 9
p + 1的地址为: 004FFA38
*/
return 0;
}
小结:
代码演示:
#include<stdio.h>
int main()
{
int a[2][3][4] = { 0 };
int *p1, (*p2)[4], (*p3)[3][4];
p1 = &a[0][0][0]; //二者等价,下同
p1 = a[0][0];
p2 = &a[0][0];
p2 = a[0];
p3 = &a[0];
p3 = a;
//指针指向的类型一定要正确,如p2为int[4]类型,
//则可以把数组的a[2][3]中的任意地址赋值给它
//但是必须有[][]
return 0;
}
6. 指向指针的指针
声明格式:
数据类型 **指针变量名;
注:有**的指针变量,称为二级指针,同理,有***的指针变量称为三级指针,依次类推.
代码演示:
#include<stdio.h>
int main()
{
char *str[3] = { "one","two","three" };
char **p = str;
for (int i = 0; i < 3; i++)
{
printf("%s\n",*(p + i));
}
/*
运行结果:
one
two
three
*/
return 0;
}