函数指针是指向函数,函数指针可以像一般函数一样,用于调用函数、传递参数。
函数指针变量的声明
typedef  (type *fun_ptr)(type,type,....); // 声明一个指向同样参数类型、返回值的函数指针类型,fun_ptr表示定义的函数指针名。
例如:
#inlcude<stdio.h>
void fun(float x,float y)
{
    printf("%f",x+y);
}
int main()
{
    float a=1,b=2.5;
    void (*P)(float,float)=&fun;   //注意声明函数指针的类型要和函数返回值类型相同,且参数类型相同
    p(a,b);
}

函数指针变量可以作为某个函数的参数来使用的回调函数就是一个通过函数指针调用的函数。

实例

实例中 populate_array 函数定义了三个参数,其中第三个参数是函数的指针,通过该函数来设置数组的值。

实例中我们定义了回调函数 getNextRandomValue,它返回一个随机值,它作为一个函数指针传递给 populate_array 函数。


#include <stdlib.h>  
#include <stdio.h>
 
// 回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; i<arraySize; i++)
        array[i] = getNextValue();
}
 
// 获取随机值
int getNextRandomValue(void)
{
    return rand();
}
 
int main(void)
{
    int myarray[10];
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}


编译执行,输出结果如下:


6807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709




在 C 语言中,😮字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。

C/C++ 中定义的字符串的内存表示:

声明和初始化创建了一个 "Hello" 字符串:
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char greeting[] = "Hello";
其实,不需要把 null 字符放在字符串常量的末尾。C 编译器会在初始化数组时,自动把 '\0' 放在字符串的末尾。   //即 char a[ ] = "hello" 不需要写出char a[ ] = "hello\0"

C 中有大量操作字符串的函数:

序号 函数 & 目的
1 strcpy(s1, s2);
复制字符串 s2 到字符串 s1。
2 strcat(s1, s2);
连接字符串 s2 到字符串 s1 的末尾。
3 strlen(s1);
返回字符串 s1 的长度
4 strcmp(s1, s2);
比较s1和s2,如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回负数;如果 s1>s2 则返回正数。
5 strchr(s1, ch);
返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置
6 strstr(s1, s2);
返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置
使用前要加头文件#include <string.h>



结构是 C 编程中另一种用户自定义的可用的数据类型
定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型
struct tag { 
    member-list
    member-list 
    member-list  
    ...
} variable-list ;

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct 
{
    int a;
    char b;
    double c;
} s1;
 
//结构体的标签被命名为SIMPLE,没有声明变量
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 COMPLEX
{
    char string[100];
    struct SIMPLE a;
};
 
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
    char string[100];
    struct NODE *next_node;
};
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
struct B;    //对结构体B进行不完整声明
 
//结构体A中包含指向结构体B的指针
struct A
{
    struct B *partner;
    //other members;
};
 
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
    struct A *partner;
    //other members;
};

为了访问结构的成员,我们使用成员访问运算符(.)。成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个句号。您可以使用 struct 关键字来定义结构类型的变量。
可以把结构作为函数参数,传参方式与其他类型的变量或指针类似.
			
			
			
struct Books
{
   char title[50];
   char author[50];
   char subject[100];
   int  book_id;
};
 
/* 函数声明 */
voidprintBook( struct Books book );
struct Books Book1;       /* 声明 Book1,类型为 Books */ printBook( Book1 );  /* 输出 Book1 信息 */

定义指向结构的指针:
struct Books *struct_pointer;
上述定义的指针变量中存储结构变量的地址:
struct_pointer = &Book1;
使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符
struct_pointer->title;

位域

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。

😮所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

典型的实例:

  • 用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。
  • 读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数
位域定义:
struct 位域结构名 
{
 
 位域列表
 
};
位域列表的形式为
类型说明符 位域名: 位域长度
例如:
struct bs{
    int a:8;
    int b:2;
    int c:6;
}data;
说明 data 为 bs 变量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位。

对于位域的定义尚有以下几点说明:

一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。

struct bs{
    unsigned a:4;
    unsigned  :4;    /* 空域————也是无名位域,它只用来作填充或调整位置。无名的位域是不能使用的 */
    unsigned b:4;    /* 从下一单元开始存放 */
    unsigned c:4
}

在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。

由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。

😮位域在本质上就是一种结构类型,不过其成员是按二进位分配的

位域的使用:
位域变量名.位域名
位域变量名->位域名
实例:
main(){
    struct bs{
        unsigned a:1;
        unsigned b:3;
        unsigned c:4;
    } bit,*pbit;
    bit.a=1;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    bit.b=7;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    bit.c=15;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    printf("%d,%d,%d\n",bit.a,bit.b,bit.c);    /* 以整型量格式输出三个域的内容 */
    pbit=&bit;    /* 把位域变量 bit 的地址送给指针变量 pbit */
    pbit->a=0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */
    pbit->b&=3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
    pbit->c|=1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
    printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);    /* 用指针方式输出了这三个域的值 */
}