Bootstrap

C语言(三)指针(二)

1.指针函数和函数指针

函数指针:把函数的这个首地址(或者入口地址)赋予一个指针变量,使用该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针称为“函数指针变量”。
定义的一般形式: 类型说明符(*指针变量名)();
简单一点就是说,函数指针通过指针变量找到函数的入口地址并调用函数。
指针函数:在C语言中,允许一个函数的返回值是一个指针(即地址),这种返回指针的函数称为指针型函数。
在这里插入图片描述
在这里插入图片描述
**eg: int * ( *(*fp)(int))[10]
fp是一个指针,指向一个函数,函数有一个整型参数,函数返回值是指针,指向有十个元素的数组,每个元素都是整形指针。
int * ( *( array[5])())()
array[5]是一个指针数组,里面有五个指针元素,指向一个函数,函数没有形参,函数返回值是指针,指向一个函数,函数没有形参(void),函数返回值是整型指针。

2.指针数组和数组指针

指针数组: 一个数组的元素值为指针,则为指针数组。
(指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量)
定义一般形式:类型说明符 * 数组名 [数组长度]
eg: int * p[3]
表示p是一个指针数组,它有三个数组元素,每个元素值都是一个指针,指向整型变量。
数组指针:指的是数组名的指针,即数组首元素地址的指针。即是指向数组的指针。
例:int (*p)[10]; p即为指向数组的指针,又称数组指针。
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。优先级:()>[]>

eg:int (*p3)[3] = &a;
int (*p4)[3] = a;
上面对p3 和p4 的使用,哪个正确呢?
p3 和p4 都是数组指针,指向的是整个数组。&a 是整个数组的首地址,a是数组首元素的首地址,其值相同但意义不同。在C 语言里,赋值符号“=”号两边的数据类型必须是相同的,如果不同需要显示或隐式的类型转换。p3 这个定义的“=”号两边的数据类型完全一致,而p4 这个定义的“=”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。
在这里插入图片描述
指针数组 用行来分,把二位数组分为两个一维数组a[0]={1,2,3}, a[1]={4,5,6} a[0]相当于p[0]
所谓的二维数组只是一个概念,在计算机中,都是一位数组排下去的。
在这里插入图片描述
数组指针是以每一行有的元素来分,也就是列。
int b[3] = {1,2,3} q = &b; * q = * (&b) ; *q = b (*q)[1] b[1]
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度
**指向二维数组的指针的几种表示方式:
&a 指向二维数组的指针
a 二维数组名,指向一维数组a[0],即第0行首地址
a[0] = * (a+0) = *a 第0行第0列元素地址
a+1 = &a[1] 第一行首地址
a[1] = *(a+1) 第一行第0列元素地址
a[1]+2 = *(a+1)+2 = &a[1] [1] 第一行第二列元素地址
*( a[1]+2)= * ( *(a+1)+2) =a[1][2] 第一行第二列元素的值

3.指针的指针

**如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针变量。(指针的指针)
eg:char **p; p前面有两个*号,相当于 *(p)。
表示指针变量p指向一个字符型指针变量。p就是指针p所指向的另一个1指针。

在这里插入图片描述
char *str是一个野指针,里面是垃圾值。但本身有个地址,比如说是0x100,
取str的地址给函数的形参,char **s本身也有一个地址,比如0x2000 ,里面是0x100
而 申请128个字节的内存给 (申请的字节有地址,比如0x10000),赋予给 *s,把0x10000赋值给 *s , char **s里面是0x10000,而地址是0x2000.让指针str指向char **s,通过里面的0x10000找到申请的这块内存。

4.命令行参数

#include<stdio.h>
int main(int argc, char *argv[])
{
	int i,j;
	int length = argc - 1;
	for(i = 1; i <= length - 1; i++)
	{
		for(j = 1; j <= length - i; j++)
		{
			if(atoi(argv[j]) > atoi(argv[j + 1]) )
			{
				char *s = argv[j];
					argv[j] = argv[j + 1];
					argv [ j + 1] = s;
				}
			}
		}
		for(i = 1; i <= length; i++)
		{
			printf("%s ",argv[i]);
		}
		printf("\n");
		return 0;
	}

atoi() 函数,把字符串转换成整数。
运行结果:
在这里插入图片描述
main函数的参数
main (int argc,char *argv[])
argc(第一个形参)必须是整型变量,
argv( 第二个形参)必须是指向字符串的指针数组。
argc参数表示了命令行中参数的个数(注意:文件名本身也算一个参数);
argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。 针数组的长度即为参数个数。
在这里插入图片描述

5.预处理与宏定义

对源程序编译之前做一些处理,生成扩展C源程序
种类 宏定义 #define; 文件包含 #include; 条件编译 #if–#else–#endif等;
在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们被称为预处理部分
一般形式: #define 宏名(参数表) 宏体
例 #define S(a,b) ab
…………
area=S(3,2);
宏展开: area=3
2;
宏展开:形参用实参换,其它字符保留
宏体及各形参外一般应加括号()

文件包含 :
功能:一个源文件可将另一个源文件的内容全部包含进来
一般形式: #include “文件名”
或 #include <文件名>
处理过程:预编译时,用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译

在这里插入图片描述
#define SQR(X) XX
int k = 2, m = 1;
SQR(k + m); // 2+1
2+1

;