学习C语言第七天

欢迎使用C语言

函数指针

函数指针是指向函数的指针变量。通常说指针是指向一个整型、字符型或数组等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参数。
函数指针变量的声明:

typedef int (*fun_ptr)(int,int);//声明一个指向同样参数、返回值的函数指针类型

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
类型 *变量名;
type是指针的基类型,它必须是一个有效的C数据类型。var-name是指针变量的名称。用来声明的星号 *与乘法中使用的星号是相同的。在这个语句中,星号是用来指定一个变量是指针。
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

回归函数

函数指针作为某个函数的参数
函数指针变量作为某个函数的参数来使用的,回归函数就是一个通过函数指针调用的函数。
回调函数是由别人的函数执行时调用你实现的函数。

C字符串

在C语言中,字符串实际上是使用null自古’\0’终止的一维字符数组。
因此,一个以null结尾的字符串,包含了组成字符串的字符。
C中有大量操作字符串的函数

序号 函数&目的
1 strcpy(s1,s2);
复制字符串s2到字符串s1
2 strcat(s1,s2);
连接字符串s2到字符串s1的末尾
3 strlen(s1);
返回字符串s1的长度
4 strcmp(s1,s2);
如果s1和s2是相同的,则返回0;如果s1 < s2则返回小于0;如果s1 > s2则返回大于0
5 strchr(s1,ch);
返回一个指针,指向字符串s1中字符ch的第一次出现的位置
6 strstr(s1,s2);
返回一个指针,指向字符串s1中字符串s2的第一次出现的位置

strlen与sizeof的区别

strlen势函数,sizeof是运算操作符,二者的到的结果类型为size_t,即unsigned int类型。
sizeof计算的是变量的大小,不受字符\0的影响;
而strlen计算的是字符串的长度,以\0作为长度判定依据。

#include<stdio.h>
#include<string.h>
void main(){
    
    
char *str1="asdfgh";
char str2[]="asdfgh";
char str3[12]={
    
    'a','s','d','s','d','s','d','s','d','s','d'};
char str4[]={
    
    "as\0f"};
int a1,a2;
a1=strlen(str1);//个数不包含\0
a2=sizeof(str1);//固定为4
printf("strlen(str1):%d\n",a1);
printf("sizeof(str1):%d\n",a2);
a1=strlen(str2);//数组个数
a2=sizeof(str2);//数组个数加上\0
printf("strlen(str1):%d\n",a1);
printf("sizeof(str1):%d\n",a2);
a1=strlen(str3);//字节个数
a2=sizeof(str3);//字节的声明
printf("strlen(str1):%d\n",a1);
printf("sizeof(str1):%d\n",a2);
a1=strlen(str4);//遇到\0就结束
a2=sizeof(str4);//数组所有\0都算,包括最后一个
printf("strlen(str1):%d\n",a1);
printf("sizeof(str1):%d\n",a2);
}

1.sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。
2.sizeof是运算符,strlen是函数。
3.sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以\0结尾的。sizeof还可以用函数做参数,如:

short f();
printf("%d\n",sizeof(f()));//输出结果是sizeof(short)

4.数组做sizeof的参数不退化,传递给strlen就退化为指针了。
5.大部分编译程序在编译时就把sizeof计算过了,是类型或者变量的长度,这就是sizeof(x)可以用来定义数组位数的原因。
6.strlen的结果要在运行时才能计算出来,是用来计算字符串的长度,不是类型占内存的大小。
7.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。
8.当使用一个结果类型或变量时,sizeof返回实际的大小;当使用一静态地空间数组,sizeof归还全部数组的尺寸;sizeof操作符不能返回动态地被分派了的数组或外部的数组的尺寸。
数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址。

小结

‘a’表示是一个字符,“a”表示一个字符串相当于’a’+’\0’;
'a’里面只能放一个字符;
“”里面表示是字符串系统自动会在串末尾补一个0。
1.字符串的本质是以\0结束的字符数组。
2.字符串的字面量是常量,比如"hello world"。

结构体

它由不同类型的数据组合成一个整体,以便引用,这些组合在一个整体中的数据是相互联系的,这样的数据结构称为结构体,它相当与其他高级语言中记录。
C数组允许定义可存储相同类型数据项的变量,结构是C编程中国另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。
结构体名,用作结构体类型的标志,它又称结构体标记,大括号内是该结构体中的各个成员,由它们组成一个结构体,对各成员都应进行类型声明:
类型名 成员名;
也可以成员列表称为域表,第一个程艳也称为结构体中的一个域。

定义结构

它相当于一个模型,但其中并无具体数据,系统对之夜不分配实际内存单元,为了能够在程序中使用结构类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。
为了定义结构,必须使用struct语句。struct语句定义了一个包含多个成员的新的数据类型。

struct tag{
    
    
member-list
member-list
member-list
...
}variable-list;

tag是结构体标签。
member-list是标准的变量定义,比如int i;或者flaot f,或者其他有效的变量定义。
variable-list结构变量,定义在结构的末尾,最后一个分号之前,可以指定一个或多个结构体变量。
在一般情况下,tag、member-list、variable-list这3部分至少要出现2个。

struct {
    
    
int a;
char b;
double c;
}s1;
//这个结构体没有标明其标签

struct SIMPLE{
    
    
int a;
char b;
double c;
};
//没有声明变量
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1,t2[20],*t3;

//也可以用typedef 创建新类型
typedef struct{
    
    
int a;
char b;
double c;
}Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1,u2[20],*u3;

//结构体的声明包含了指向自己类型的指针
struct NODE
{
    
    
char string[100];
struct NODE *next_node;
};

//两个结构体互相包含,则需要对其中一个结构体进行不完整声明
struct B;
struct A{
    
    
struct B *partner;
};
struct B
{
    
    
struct A*partner;
}

1.类型与变量是不同概念,不是混同,只能对变量赋值,存取或运算,而不能对一个类型赋值,存取或运算。在编译时,对类型是不分配空间,只对变量分配空间。
2.对结构体中的成员(即域)可以单元使用,它的作用与地位相当于普通变量。
3.成员也可以是一个结构体变量。
4.成员名可以与程序中的变量名相同,二者不代表同意对象。

结构体变量的初始化

和其他类型变量一样,对结构体变量可以在定义时指定初始值

结构体变量的引用

1.不能将一个结构体变量作为一个整体进行输入和输出。
只能对结构体变量中的各个成员分别进行输入输出。引用结构体变量中的成员的方式:结构体变量名.成员名
.是成员(分量)运算符,它在所有的运算符中优先级最高。
2.如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。只能对最低的成员进行赋值或存取以及运算
3.对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
4.可以引用结构体变量成员的地址。也可以引用结构体变量的地址。
不能用一下语句整体读入结构体变量:
scanf("%d,%s,%c,%d,%f,%s",&student1);

访问结构成员

为了访问结构的成员,使用成员访问运算符(.)。成员访问运算符是结果变量名称和要访问的结构成员之间的一个句号。

结构作为函数参数

可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。

指向结构的指针

可以定义指向结构的指针,方式与定义指向其他类型变脸的指针相似。使用指向该结构的指针访问结构的成员,必须使用->运算符。
在C语言中,为了使用方便和使之直观,可以把(p).num改用p->num来代替,它表示p所指向的结构体变量中的num成员,同样,(*p).name等价于p->name。
也就是 结构体变量.成员名 等价于 (*p).成员名 等价于 p->成员名
p->n得到p指向的结构体变量中的成员n的值
p->n++得到p指向的结构体变量中的成员n的值,用完值后使它加1
++p->n得到p指向的结构体变量中的成员n的值使之加1(先加)

结构体小结

结构体变量的首地址能够被其最宽基本类型成员的大小整除。

结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节。即结构体成员 的末地址减去结构体首地址(第一个结构体成员的首地址)得到的偏移量都要是对应成员大小的整数倍。

结构体的总大小为结构体最宽基本类型成员的整数倍,如有需要编译器会在成员末尾加上填充字节

结构体中成员变量分配的空间是按照成员变量中占用空间最大的来作为分配单位,同样成员变量的存储空间也是不能跨分配单位的,如果当前的空间不足,则会存储到下一个分配单位中。

编译时,系统会根据给出的初值的结构体常量的个数来确定数组元素的个数。一般先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。

结构体数组

一个结构体变量中可以存放一组数据。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前的数据值型数组不同之处在于每个数组元素都有一个结构体类型的数据,它们分别包括各个成员(分量)项。

位域

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态,用1位二进制位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为"位域"或“位段”。
所谓“位域”是把 一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名允许在程序中am域名进行操作。可以把几个不同的对象用一个字节的二进制位域来表示。
用1位二进位存放一个开关量时,只有0和1两种状态。
读取外部文件格式——可以读取非标准的文件格式。

位域的定义和位域变量的说明

位于定义与结构定义相仿,其形式为:

struct 位域结构名{
    
    
位域列表
};

其中位域列表的形式为:
类型说明符 位域名 :位域长度

1.一个位域存储在同一字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元其存放该位域。也可以有一使某位域从下一单元开始。
2.由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8为二进位。如果最大长度大于计算机的整体字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。
3.位域可以是无名位域,这时它只用来填充或调整位置。无名的位域时不能使用。

位域的使用
位域允许用各种格式输出。
####用结构体变量和指向结构体的指针作函数参数
将一个结构体变量的值传递给另一个函数,有3个方法:
(1)用结构体变量的成员作参数,例如:用stu[1].num或stu[2].name作函数实参,将实参值传给形参。用法和用普通变量作实参是一样,属于值传递方式。应当注意实参与形参的类型保持一致。
(2)用结构体变量作参数。但是用结构体变量作实参时,采取的是值传递的方式,将结构体变量所占的内存单元全部顺序传递给形参。这种传递方式在空间和时间上开销较大,如果结构体的规模很大时,开销是很客观的,此外由于采用值传递方式,如果在执行被调用函数器件改变了形参(也是结构体变量)的值,改制不能返回主调函数,这往往造成使用上的不便。因IC一般较少用这种方法。
(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。

结构体大小的计算

结构体默认的字节对齐一般满足三个准则:
(1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
(2)结构体每个成员相当于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
(3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding).

结构体内存分配原则

原则一: 结构体中元素按照定义顺序存放到内存中,但并不是紧密排列。从结构体存储的首地址开始,每一个元素存入内存中时,它都会认为内存是以自己的宽度来划分空间的,因此元素存放的位置一定会在自己大小的整数倍上开始。
原则二: 在原则一的基础上,检查计算出的存储单元是否为所有元素中最宽的元素长度的整数倍。若是,则结束;否则,将其补齐为它的整数倍。
(1)定义位域时,各个成员的类型最好保持一致,比如都用char,或都用int,不要混合使用,这样才能达到节省内存空间的目的。

猜你喜欢

转载自blog.csdn.net/qq_31932681/article/details/95078905