C语言:
1:数据类型 int short char long ....
2:运算符 + - * / % += ++ -- -+ /= *=
3:九大控制流语句
1 if
2 for
3 while
4 do... while()
5 break
6 switch case
7 continue
8 return
9 go to
3:数组
4:函数
5:指针
6:数组 指针 函数之间的关系
7:宏定义 #define a(x,y) x>y?x:y;
数据类型
1、整形数:
没有小数点的:
short :有符号短整型 signed short int 2个字节
int:有符号整形 signed int 4个字节
最高位为符号为: 0:正数 1:负数
unsigned int:无符号整形 (一定是正数),取值范围是有符号整形正数范围的两倍
long: 有符号长整形 signed long int 4个字节
long long 8个字节
有小数点的(浮点数)
flaot 4个字节 (单精度)
double 8个字节 (双精度)
字符型:
char 1个字节
char xxx= 'S';
ascii
数据类型变量的定义、初始化、赋值
变量的定义:
定义一个整形类型的变量a int a;
赋值 a a = 10;
a = 0x00ff0000;
a = 011;
int:可以存放 十进制 %d 十六进制 %x 八进制 %o 二进制
2、类型转换:
低精度转成高精度,数据不会丢失
高精度转成低精度,数据会丢失
低精度 高精度
char | short --> int --> long --> long long -->float -->double
自动转换
挡在赋值运算或者混合运算时候,会数据类型会自动转换
赋值运算:等于号右边的类型自动变成等于号左边的类型
混合运算:低精度自动高精度的类型
强制转换
int a =10;
long b = (long)a;
printf("%f\n",(float)a);
1)基本数据类型
int short char long double float
(32bit)内存中占用的字节数 4 2 1 4 8 4
(64bit)内存中占用的字节数 4 2 1 8 8 4
采用一个操作符sizeof来进行计算。
#include
int main(void)
{
printf("sizeof(int): %d\n", sizeof(int));
return 0;
}
编写的程序只是人类所能识别的一些操作。
机器只识别二进制编码。
需要将C语言转化为二进制编码,让机器能够识别并完成相应的操作。
编译生成可执行的程序命令:
gcc hello.c -o hello
需要编译器:GCC来完成。
一共4个步骤:预处理、编译、汇编和链接
预处理:展开头文件,将头文件中的信息导入要预处理的文件。
宏定义的替换。
条件编译:如果#if 0,则#endif前,#if 0后面的代码不参与编译。
编译:将C语言程序编程汇编语言。
汇编:将汇编程序变成机器识别的二进制编码。
链接:将生成的二进制编码链接相应的库生成可执行程序。
执行可执行程序:./hello
gec@ubuntu:/mnt/hgfs/share$ ./hello
One
hello world!
The number is 3
gec@ubuntu:/mnt/hgfs/share$
2)变量
变量:存储空间的分配。确定空间的大小和存储的数据类型。
这一块空间中存储的数据是可以改变。(存储空间内的数据可变化的量)。
变量的命名规则:以字母,下划线开头,由数字,字母,下划线组成。
变量名保留的规则:《C语言变量名保留关键字》
32个关键字,每个关键字都有不同的含义:
1)非常见类:auto, register, voliate, goto
2)存储相关:const, extern,auto
3)数据类型:int,char,float
4)逻辑控制:if, else, for, while
5)特殊用途:sizeof, typedef
int i;
char j;
1)int类型数据。(存放一些整型的数值)
2)char类型数据。
1)存储一些整型的数据。
1个字节的数据能存储多大的数值?
1byte = 8bit
0000 0000 -------> 0
0000 0001 -------> 1
0000 0010 -------> 2
……
1111 1111 -------> 2的8次方-1 =256 --->0~255
char i = 255;
printf("i = %d\n", i);
gec@ubuntu:/mnt/hgfs/share$ gcc char.c -o char
gec@ubuntu:/mnt/hgfs/share$ ./char
i = -1
gec@ubuntu:/mnt/hgfs/share$
数值的零界问题。--->数据溢出
进制:二进制,八进制,十进制,十六进制。
二进制:机器能够识别的进制。(B)只包含0,1
八进制:0~7。(O)
十进制:人类看得懂的所有数字数据。(D)0~9
十六进制:编程经常使用的一种进制。(X)0~9, A~F
原码,反码,补码。
所有的数据在计算机中都是以补码形式进行存储。
1)正数:正数的原码,反码,补码一致。
2)负数:
负数的原码:255 -----> 1 111 1111(最高位默认为符号位)
负数的反码:符号位不变,其它位取反 1 000 0000 (符号位:1 负数 0 正数)
负数的补码:反码+1 -----> 1 000 0001 ----> -1
2)字符编码:ASCII
字符:'a' , 'b'
字符串:"a", "b"
"a"与'a'的区别?
字符:存储1个字节单位的数值。a-->97
字符'0'~字符'9'ASCII
字符'A'~字符'Z'ASCII
字符'a'~字符'z'ASCII
字符串:存储多个字符。
"a"--->存储2个字节的数据('a' 和 '\0')
3)转义字符
'\n' --->换行
'\t' --->tab
……
3)修饰类型
1)有符号与无符号类型。
int a;(默认是有符号的类型)
unsigned int a;
在于取值的范围不同。
char类型占用1个字节。(0~255)
char a;(-128~127);
unsigned char a;(0~255);
原码,反码,补码相关知识点。---->计算机存储数据
2)自动类型(局部变量)和静态类型(全局变量)
自动类型(局部变量):程序运行的时候占用空间(auto 关键字(系统默认添加,不需要自己填写))
静态类型(全局变量):程序编译的时候占用空间。
显示静态变量:static关键修饰的变量-->程序编译的时候占用空间。
例如:static int a = 100;
用途;
局部变量,适合在函数体中重复调用,记录历史数据,提高访问的安全性。
全局变量:适用于本文件的私有调用(不能够在其他文件中使用这个变量)。
如果在定义的时候未初始化,系统会自动的初始化为0。
显示静态变量:运用于函数,使用语本文件的私有保护。
3)寄存器类型(register)
特点:资源少,运算的速度,无地址(不需要地址操作)
数据初始化: 关键字 类型 变量名 = 数值。
例如:register int e = 5;
4)运算符
1)取反(按位):~
举例:1000 0101 ---> 0111 1010
练习:十进制数12取反后的数值是多少?
笔算数值是多少?再用代码进行验证。
12 十进制-----> 0000 1100 二进制
~0000 1100 ---> 1111 0011存储在计算机中。(补码)
显示数值的时候是以原码来显示的。
-13 ---> 1000 1101 原码
1111 0010 反码
1111 0011 补码
13 ---> 0000 1101 (补码,原码,反码)
练习:
进制 原码 反码(~)
B 0101 0101
O 0125
D 85
X 0x55
2)自增自减运算符(++,--)
自加运算符
1)
int sn = 1;
printf("sn = %d\n", sn++);//现赋值,在自加
2)
int sn = 1;
printf("sn = %d\n", ++sn);//先自加,在赋值
自减运算符
1)
int sn = 1;
printf("sn = %d\n", sn--);//现赋值,在自减
2)
int sn = 1;
printf("sn = %d\n", --sn);//先自减,在赋值
错误的编程方式:
5++;(常量不可自加)
3)取非运算符(逻辑 真与假) !
计算机对真假的判定:0为假 非零为真
判断一个数是否为真或假:
int data = 0;
printf("data = %d\n", !!data);
4)取模或取整运算符(% /)
取模运算: 整数%整数
取整运算: 整数/整数
123%10 = 12 --- 3
举例:
int a, b;
a = 123%10;
printf("a= %d\n", a);
b = 123/10;
printf("b= %d\n", b);
练习:
输入一个大于10000的整数,分别提取出万位,千位,百位,十位,个位的数值。
5)移位运算 << >>
左移:(最右边的数填零,做左边的数舍去)
char data = 0x1; ----> 0000 0001
data << 1; ----> 0000 0010
右移:(最右边的位数舍去,最左边的数值符号位补充
char data = 0x2; ----> 0000 0010
data >> 1; ----> 0000 0010
具体:
颜色:三原色所构成(RGB--->Red,Green,Blue)
黄色:R-->255 G--->255, B--->128
显示BMP格式的图片,1个像素占用3个字节。(每一个字节占用1个比例色素)
1byte = 8bit
1个BMP图片的像素 = 3byte = 24bit
BGR:1000 0000 1111 1111 1111 1111 B
0x80ffff H黄色
8421码来进行数值的转化:
1 0 0 0 ---> 0x8
8 4 2 1
0000 ~ 1111
二进制 十六进制
0000 ----> 0
0001 ----> 1
0010 ----> 2
0011 ----> 3
1000 ----> 8
1001 ----> 9
1010 ----> a
……
0x80ffff << 8,变成 0xffff00
1000 0000 1111 1111 1111 1111 <<8
------>1111 1111 1111 1111 0000 0000
------>0xffff00
6)位操作:与& 或| 异或^
1)按位与&:当两个数都为真,结果才为真。(清零)
0100 1010
& 1010 0111
------------------------
0000 0010
按位或|:只要有1个为真,结果就为真。(置1)
0100 1010
| 1010 0111
------------------------
1110 1111
异或^: 相同为假,不同为真。
0100 1010
^ 1010 0111
------------------------
1110 1101
异或操作一般应用于金融IT领域,对数据进行加密操作的时候。
加密后的数据 + 密钥 --->原始数据
原始数据 + 密钥 --->加密后的数据
练习:
有一个数值为0101 0111,将该数值的第五位清零,并将第六位置1。
char data = 0x57;
data = data & ~(0x1<<5);
0101 0111
& 1110 1111
--------------
0100 0111
~(0x1<<5):1110 1111
0x1<<5--> 0000 0001<<5 --> 0001 0000 ---> 1110 1111
data = data | (0x1<<6);
0101 0111
| 0010 0000
--------------
0111 0111
0x1<<6--> 0000 0001<<6 --> 0010 0000
练习:
编译一个程序,实现两个数的置换。
int a =10; //0000 1010
int b =20; //0001 0100
通过一个简单的算法实现
int a存储int b中的值。
int b存储int a中的值。
a = a^b;
0000 1010 a
^ 0001 0100 b
-----------------
0001 1110 mid
b = b^a;
0001 0100 b
^ 0001 1110 mid
-----------------
0000 1010 b-->
a = a^b;
0001 1110 mid(a)
^ 0000 1010 b-->
-----------------------
0001 0100
int a =10; //0000 1010
int b =20; //0001 0100
异或之后的结果:
a = 0001 0100
b = 0000 1010
7)三目运算符
一般格式:表达式1 ? 表达式2 : 表达式3
表达式1:一般为逻辑判断(非真即假)
表达式2:若表达式1为真,则执行表达式2中的内容。
表达式3:若表达式1为假,则执行表达式3中的内容。
例子,两个数比大小,通过三目运算符来比大小。
int a =3;
int b =4;
写法一:
if(a>b)
{
printf("a>b");
}
else{
printf("ab) ? printf("a>b"); : printf("a
一: \n换行符需要注意的地方:
示例代码:
while(1)
{
sleep(1);
printf("HELLO WORLD");
}
A:会打印 B:不会打印
printf能打印的条件总结
1:printf是遇到/n 打印
2:printf 和 sancf配合 可以打印
3:printf对应的函数退出时 可以打印
4:当内存里面的标准输出缓存区满时,可以打印 (需要等待)
二、scanf的返回值:
计算键盘输入的有效数据个数
键盘输入 int scanf("%d",&xxx);
long scanf("%ld",&xxx);
char scanf("%c",&xxx);
三:getchar: 键盘接受字符(char),一个字符是一个字节
例子:
char ch;
ch = getchar();//getchar函数的返回值就是你键盘输入的字符
练习:
1:利用getchar函数,编写一份实现清空缓存区的代码
2:利用sacnf和getchar,实现键盘输入年龄数字,,判断改年龄是否大于100,如果大于100.则打印你是妖怪吗?,如果户用输入的数据不正常,需要实现能重新输入的功能。
不正常的数据: a 19abcdfs 13 23
=================================================================================================================
1、函数的定义 封装 调用 (声明)
1.1、函数的定义(函数不能嵌套定义)
main函数:
正常: char main() int:整形 函数的返回值类型 函数的返回值与函数的返回值类型要一致
{
return 's';
}
自己自定义一个函数(给函数取名字)
练习:
自己定义一个函数,实现判断键盘输入的数是不是偶数,如果是则返回1,如果不是则返回-1;
check_num();
1.2:函数的封装 (实现函数的功能)
1.3:函数的调用 (使用某个函数)
函数是在函数里面被调用的,函数可以嵌套调用
调用者:main (main只能做调用者,不能做被调用者)
被调用者: check_num
2、全局变量、局部变量
全局变量是在函数外面,默认初始化的值为0。
局部变量是在函数里面的,只属于该函数的。(定义局部变量时,最好给他赋值)
练习:
1、利用函数的嵌套调用 编写实现 求5的 阶乘的代码 1x2x3x4x5x6
2、修改之前的显示时间的代码,封装成三个函数,分别计算时、分、秒
第一种修改:
不能用传参 和返回值
第二种修改:
只用用传参和返回值
第三种:
只能用返回值 static:静态变量:只初始化一次
3、控制流语句
int num =0;
for(num=10;num<19 ; num++)
嵌套的for循环
for(int a=56; a<65; a++) 9
{
for(int b=86; b>68; b--) 18 9 *18
{
printf("HELLO WORLD\n");
}
}
for(; ;) while(1)
{ {
} }
for(; ; ); while(1);//堵塞
一、控制流语句的练习:
1:键盘输入一个数,然后颠倒打印出来。例如:输入 564 输出 465
2:计算 1+2+3+....+100的和,并且输出
补充:运算符
1:算术运算符 + - * / %(取余数) += -= /= *= %= ++ --
2:逻辑运算符 && || !
练习:持续键盘输入一个年号,判断是闰年还是平年。
闰年: 只能被4整除并且不能被100整除 或者只能被400整除
3: > >= < <= == !=
4:三目运算符(唯一一个三目运算符)
条件语句?变量1:变量2
条件语句?执行语句1:执行语句2
练习:运用三目运算符,把两个整形变量的值互换。
二、
1、
switch(变量)
{
case 情况1: 执行语句;break;
case 情况2: 执行语句;break;
case 情况3: 执行语句;break;
case 情况4: 执行语句;break;
case 情况5: 执行语句;break;
default:执行语句
}
2、 先执行,再判断,至少执行一次
do
{
}while();
用do while(),键盘输入一个数,打印出这个数的范围内所有能被5整除的数。
3、goto
练习:使用goto 实现死循环打印helloworld
int num=0;
loop:
printf("HELLO WROLD\n");
if(num++ == 3) goto others;
goto loop;
others:
。。。。。。。
=================================================================================================================================
三、数组
数组的特点:
1:他可以存放一个或多个同一种类型的数据
2:通过数组下标来访问数组里面的成员数据 数组下标的范围是 0 ~ 数组的长度-1 a[10]
3:数组下标可以越界 ,编译不会出错,但是运行的时候有可能出错
4:数组里面每个元素的地址是连续的 首先:如何打印变量的在内存里面的地址
例子:定义一个能够存放 10个整形数据的数组 int array[10];
练习:
1: 定义一个能够存放10变量的整形数组,里面的数据自己随机赋值,找出里面最大的数
2: 定义一个能够存放10变量的整形数组,把里面的数据从大到小重新排列
a[4] = {33, 55 ,44,4}
第一次判断 33 和 55 33 和 44 33 和 4
第二次: 55 44 55 4
最后一次 44 4
左边 和 右边判断
0 ~ 2 1 ~ 4
int i,j;
int max=0;
for(i=0; i
1、打印hello world的代码:
#include
int main()
{
printf("HELLO WORLD\n");
reutrn 0;
}
#include 是 预编译命令 在编译之前把include后面加的文件,包含进.c文件里面
预处理编译命令(头文件包含、宏定义的展开): gcc -E hello.c -o hello.i
编译汇编(检查语法): gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
链接(链接程序运行时所需要的库) gcc hello.o -o hello
2、C语言中的函数:
函数分为两种:主函数(调用者)、子函数(被调用者)
传参:函数之间,传递数据
实参: 属于调用者的局部变量
形参: 属于被调用的局部变量 ,是用来存放调用者传递过来的数据 ,形参和实参的类型一定要一致
int fun(int j)//自己自定义一个函数
{
printf("This a fun\n");
printf("main函数传过来的数据是:%d\n",j);
return 0;
}
int main()
{
int a =110; //a是实参
fun(a);
return 0;
}
练习:
在main里面键盘输入一个数,把这个数传给fun函数,让fun函数判断传过来的数是不是偶数。
C语言编程模块化(多个文件):
模块化:按功能分类
例子:打印HELLO WORLD
main.c 写main函数
hello.c 打印hello
world.c 打印world
helloworld.h 声明 打印hello和world的函数
数组:
数组不能动态定义
int n;
scanf("%d",&n);
int array[n];
======================
数组没有赋值的元素,默认会赋值为0.
int array[3] = {2};
array[2] = ??;
======================
数组名是数组首元素的地址,但是有时候他也表示的是整个数组
1:自己自定义一个存放10个随机整数的数组,把里面所有偶数打印出来
2:自己自定义一个存放10个随机整数的数组,把里面的偶数拿出来存放到另外的数组里面
======================
字符数组:
char array[] = “中国”;//定义一个空的数组
char array[5] = {'0','2','s','w','\0'};
char array[5] = "02sw"
printf("%c\n",array[2]);
printf("%s\n",array);
练习: 通过循环,把数组里面的每个字符打印出来
Linux 中文编码 UTF-8 一个中文占3个字节
windows 中文编码 GB2312 一个中文占2个字节
如何定义一个字符串存放在数组里面? char arrray[] = “dfasfhjfkash”;
字符串数组默认最后的一个字节是什么数据? \0
练习:字符数组元素的遍历,实现的功能有一下参见的几点:
1:字符去重复(相邻两个相同的字符)
2:搜索里面个有没有某个字符,出现的个数是几次
字符串操作函数:
bzero(首地址,要清除的长度);
memset(首地址,想要改成的字符,要清除的长度);
整形数组: 0 -1
字符: 任意字符
strlen :计算字符串的长度
strcat(数组1,数组2); :把数组2拼接带数组1后面
strcpy(数组1,数组2); 把数组2复制到数组1里面
strcmp(数组1,数组2); 判断两个字符串相不相等
等于0: 想等
小于0: 数组1<数组2
大于0: 数组1>数组2
指针:
1:怎么定义一个指针变量 int a=10; 整形变量
指针变量用来存放指针(指针就是变量地址)
指针变量要和存放的变量地址的类型要一致
整形的指针变量: int * 变量名
字符的指针变量: char * 变量名
int A;
char B;
int *a = &B;//指针变量初始化
A 是什么? 整形变量 *a 就等于 A
&A是什么? 整形变量A的地址 a 就等于 &A
a:是什么? 指针变量a,这个a里面存放的是整形变量A的地址 a 就等于 &A
&a是什么? 指针变量a的地址
*a是什么? 整形变量的数据 *a 就等于 A
char *b
2:指针变量是用来干嘛?
2:指针 (指针就是变量地址)
一、
指针变量用来存放地址的
如何定义一个整形指针变量: int *p = NULL;
野指针怎么样出现?就是在定义指针变量的时候,没有自己赋值一个地址给他,那么系统就会随机给他一个地址。
避免野指针: int *p = NULL;
野指针:这个指针变量存放的地址是不合法的。
给指针变量赋值地址的做法:
1:初始化:
int a=10;
int *p = &a;
2:赋值:
int a 10;
int *p =NULL;
p = &a;
===================================================================================================
1、指针变量的传参
2、指针变量作为函数的返回值
练习:
定义一个函数,返回值
3、
char *A[] = "dhfakhdflsa";
char *p = &(A[0]);
printf("%c",*p);
1:让指针变量p存放数组首元素的地址 &(A[0]); A;
2:让指针变量p存放的是数组的首地址,那指针变量P要是一个数组指针 &A
练习:
1、通过指针实现 键盘输入一个字符,在一个字符串里面搜索有没有该字符,有则把这个字符变成' '
2、编写一份实现类似strcpy函数功能的代码
在32位系统上,char *p ; 指针变量占__4 _字节,指针占__1__字节.
补充:数组本来就是个一级指针
int A[5] = {2,44,66,77,3,2};
A[3];
*(A+3)
*(A+1)
*(A++) 错
int a[4] = {1,4,6,7};
(*(&a))[1] 等于 4 a[1] int a = 10; *(&a) a
*((*(&a))+1) 等于 4
*(a+1)
1、int a[4] = {1,4,6,7};
以(*(&a))[1]为例子:
&a:数组的首地址
*(&a):数组名、数组a、数组的首元素的地址
(*(&a))[3]:数组a下标为3的元素
*((*(&a))+1):通过数组首元素的地址往下移动一个位置,得到数组第二个元素的地址,在解引用获取到
数组第二个元素的数据
*((*(&a))+1):_____________。
============================================================================================
一、
函数与指针的关系
1:指针函数:指针函数他是一个函数,然后其返回值为指针类型
int* fun()
{
int a;
//int *p =&a;
return p;//return &a;
}
例子:自己自定义一个指针函数,该函数返回值为一个指向字符数组首元素的地址的指针变脸
2:函数指针:函数指针他是一个指针,指向一个函数的
二、数组与指针的关系
数组指针:数组指着他是一个指针,指向一个数组
int array[4] = {1,44,56,77};
int (*p)[4] = &array; //定义一个数组指针指向 array p就是存放数组的地址
printf("%d\n",(*p)[2]);
printf("%d\n",*((*p)+2));
练习: 利用刚学的数组指针为函数的形参进行传参。
指针数组:他是一个数组,存放多个指针变量
int a=19,b=11,c=112;
int * array[3] = {&a,&b,&c};//定义一个指针数组
一、结构体
int a ;整形
int a[10];整形数组
1、结构体数据类型怎么定义? 特点:可以存放多种多个数据类型
struct a
{
int b; 4
char c; 1
long long d; 8
};
2、上面定义的结构体在内存中的大小是?
Linux下,计算结构体的大小的规则:
32位系统安装不了64编译器,64位系统可以安装32位编译器
32位编译器:
1:以内存中4个字节对齐
2:计算出来的总大小要是里面最大的数据类型的倍数(倍数对大为4)
64位编译器:
1:以内存中8个字节对齐
2:计算出来的总大小要是里面最大的数据类型的倍数
3、计算结构体的大小的规则,是以编译器的位数决定的,和系统的位数无关
32位:gcc 4.6.3 32位的编译器以 4个字节对齐
64位:gcc 5.4.0 32位的编译器以 8个字节对齐
4、typedef与结构体的一起用
typedef struct my_struct
{
int a;
}S_m,*P_S_m;
S_m:
struct my_struct 等于 S_m
struct my_struct A;
S_m A;
P_S_m:
P_S_m 等于 struct my_struct *
struct my_struct *B;
P_S_m B;
5、结构体传参
typedef struct A
{
int b;
}a;
int fun(a st,int b)
{
printf("%d----%d\n",st.b,b);
return 0;
}
int main()
{
a st;
st.b =110;
fun(st,st.b);
return 0;
}
6、结构体指针传参
typedef struct A
{
int b;
}a,*p_a;
int fun(p_a st,int b)
{
printf("%d====%d\n",st->b,b);
return 0;
}
int main()
{
p_a st = (p_a)malloc(sizeof(a));
st->b = 110;
fun(st,st->b);
free(st);
return 0;
}
一、结构体数组
1.1
概念: 他是一个数组,里面可以存放一个或多个结构体,但是这些结构体是同一种类型的结构体
1.2
结构体数组怎么定义?
1、定义一个结构体的类型
struct A
{
int a;
char b;
}
2、定义一个结构体的数组,以及使用它
struct A st[5];
st[0].a = 10;
st[1].a = 11;
printf("%d----%d\n",st[0].a,st[1].b);
1.3、练习:
自己定义一个结构体数组,里面存放的结构体类型设计成存放学生的基本信息(学号、性别、年龄),把5个学生的基本信息打印出来。
1.4、结构体数组指针
正常定义一个结构体数组指针
typedef struct A
{
int a;
} S_A,*P_S_A,(*P_S_A_R)[];
int main()
{
struct A *st_array_p[] = (struct A (*)[]) malloc(sizeof(struct A)*2);
scanf("%d",&(*st_array_p)[1].a));
printf("%d\n",(*st_array_p)[1].a));
return 0;
}
用typedef定义一个结构体数组指针
int main()
{
//st_array_p是一个数组指针变量,存放的肯定是数组的地址
//*st_array_p获取到数组,也就是表示数组名,也就是数组的首元素地址
P_S_A_R st_array_p[] = (P_S_A_R ) malloc(sizeof(S_A)*2);
scanf("%d",&(*st_array_p)[1].a)); //(*st_array_p)[1] 数组里面下标位1的元素(结构体)
scanf("%d",&((*st_array_p)+1)->a); int a[2]; &a a &(a[0])
printf("%d\n",(*st_array_p)[1].a));
return 0;
}