C语言

学习 static 定义静态变量的用法

在 C 语言中,static 关键字用于声明静态变量。静态变量与普通变量不同,它们的生存期和作用域是不同的。
静态变量在声明时被初始化,只被初始化一次,而且在整个程序的生命周期内都保持存在。在函数内声明的静态变量只能在该函数内访问,而在函数外声明的静态变量则只能在该文件内访问。
eg:
以下实例中 foo() 函数声明了一个静态变量 x,并将其初始化为 0。每次调用 foo() 函数时,x 的值都会加 1,并打印出新的值。由于 x 是静态变量,它在程序的整个生命周期中都存在,而不仅仅是在函数调用时存在。因此,每次调用 foo() 时,它都可以记住 x 的值,并在此基础上递增。

#include <stdio.h

void foo()
{
    static int x = 0;
    x++;
    printf("%d\n", x);
}

int main()
{
    foo();  // 输出 1
    foo();  // 输出 2
    foo();  // 输出 3
    return 0;
}

在这里插入图片描述

隐形刺客:auto

描述:这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。auto关键字在我们写的代码里几乎看不到,但是它又无处不在,它是如此的重要,又是如此的与世无争,默默的履行着自己的义务,却又隐姓埋名。

Tips:auto存储类是所有局部变量默认的存储量
使用auto进行类型推断
(1)普通类型的推断

auto x = 127; // auto 被推断为 int 类型
auto y = 3.14; // auto 被推断为 double 类型
***auto z; // 错误,必须有初始值才能进行推断***

(2)引用类型推断

int x = 0, &rx = x;
auto a1 = rx; // 使用引用其实是使用引用的对象,此时 auto 以引用对象的类型作为 auto 的类型,所以 auto 这里被推断为 int
auto &a2 = rx; // 此时 auto 被推断为 int 类型,a2 对象本身就是 int & 类型
const auto &a3 = rx; // auto 被推断为 int 类型,a3 对象本身是 const int & 类型,不能通过 a3 去修改 rx 引用的对象值

(3)const类型的推断

const int c = 10;
***auto a1 = c; // auto 此时是 int,顶层 const 属性被丢弃
auto &a2 = c; // auto 此时是 const int,底层 const 属性保留***

(4)针对数组和函数的推断

***const char arr[] = "I Love China";
auto r1 = arr; // 如果将数组名赋值给 auto 变量,那么 auto 推断的结果是指针类型***,如果有 const 属性
// 会被保留,auto 推断的结果是 ***const char *,r1 为 const char*** *
auto &r2 = arr; // 如果将数组名赋值给 auto & 变量,auto & 变量的类型是一个数组引用类型,即为
// const char (&) [14]
int add(int a,int b); // 函数声明
auto r3 = add; // r3 为 int(*)(int, int)
auto &r4 = add; // r4 为 int(&)(int, int)

子安的个人笔记:auto真没用!
容易产生编译错误或者编译器报错。存在版本不兼容的情况。

在这里插入图片描述

在这里插入图片描述

register的运用:

"register"是C语言中的一个关键字,用于向编译器建议将变量存储在寄存器中而不是内存中。然而,在现代编译器中,编译器通常对变量的存储位置进行自动优化,因此使用"register"关键字往往没有实际的效果。

由于编译器通常能够更好地决定变量的存储位置,使用"register"关键字可能被忽略或被视为无效的组合。实际上,C语言标准从C99版本开始已经废弃了"register"关键字,并将其视为普通的存储类别说明符。

因此,如果代码中出现"register"关键字,并且编译器报告类型说明符的组合无效,那么这可能是因为"register"关键字在现代编译器中已经不再起作用,应该考虑删除它或使用其他更合适的存储类别说明符。

补充上方红字部分的寄存器和内存的区别:(大概看看)
1、 存储位置:内存是计算机中用于存储数据和程序的物理硬件设备,它通常位于主板上,以芯片组或者扩展插槽的形式存在。而寄存器是CPU内部的一种高速缓存,位于CPU内部的寄存器组中。
2、 容量:内存通常具有较大的容量,以GB为单位。寄存器容量较小,每个寄存器的容量通常是以字节为单位,总容量取决于CPU的架构和设计。
3、 访问速度:由于内存是通过总线连接到CPU,访问内存的速度相对较慢。而寄存器是位于CPU内部并与其紧密集成的,在CPU设计中通常使用高速的SRAM(静态随机存储器)实现,所以访问寄存器的速度非常快,比访问内存快得多。
4、 使用方式:内存用于存储各种数据和程序,包括变量、常量、数组、函数等。寄存器通常用于存储CPU正在执行的指令、数据的临时结果和CPU的状态信息等。
5、 可见性:内存是存储在计算机的物理硬件中,可以被操作系统和其他程序访问。寄存器是CPU的一部分,对于其他硬件和软件来说是不可见的,只能由CPU自己直接访问。

“野指针”

“野指针”(wild pointer):是没有被初始化过的指针,所以不确定指针具体指向。例如以下示例代码:

void *p; // 此时 p 是“野指针”

因为“野指针”可能指向任意内存段,因此它可能会损坏正常的数据,也有可能引发其他未知错误。在实际的C语言程序开发中,定义指针时,一般都要尽量避免“野指针”的出现,可通过赋初值方式解决:

void *p = NULL;
void *data = malloc(size);

“悬空指针”

“悬空指针”(dangling pointer):是指针最初指向的内存已经被释放了的一种指针。通俗点可以理解:指针指向一块内存,如果这块内存稍后被操作系统回收(被释放),但是指针仍然指向这块内存,那么,此时该指针就是“悬空指针”。例如以下示例代码:

void *p = malloc(size);
assert(p);
free(p); 

// 现在 p 是“悬空指针”
C语言中的“悬空指针”会引发不可预知的错误,而且这种错误一旦发生,很难定位。这是因为在 free§ 之后,p 指针仍然指向之前分配的内存,如果这块内存暂时可以被程序访问并且不会造成冲突,那么之后使用 p 并不会引发错误。

所以在实际的C语言程序开发中,为了避免出现“悬空指针”引发不可预知的错误,在释放内存之后,常常会将指针 p 赋值为 NULL:

void *p = malloc(size);
assert(p);
free(p); 
// 避免“悬空指针”
p = NULL;

typedef关键字

在C语言中,typedef关键字用于定义新的类型别名。它可以为已经存在的数据类型创建一个新的名称,以方便在程序中使用。
typedef的一般语法如下:

typedef existing_type new_type;

existing_type是已经存在的数据类型,new_type是你定义的新的类型别名。
下面是一些typedef的使用示例:

typedef int Integer;  // 将int类型定义为Integer类型别名

typedef struct {
    int x;
    int y;
} Point;  // 将结构体定义为Point类型别名

typedef void (*FunctionPtr)();  // 将函数指针定义为FunctionPtr类型别名

typedef enum {
    RED,
    GREEN,
    BLUE
} Color;// 将枚举定义为Color类型别名

在上面的示例中,我们使用typedef关键字为不同的数据类型创建了新的类型别名。这样,在程序中可以使用新的类型别名来声明变量,而不必直接使用原始的数据类型。
使用typedef可以提高代码的可读性和可维护性。通过为复杂的数据类型创建更直观的别名,可以使代码更易于理解和调试。

如何在输出内容中输出 % ?

在C语言的printf函数中,如果要输出一个百分号%,需要用两个连续的百分号%%来表示。这是因为%字符在格式化输出中有特殊的含义(用于指示格式化输出的开始),所以如果要输出一个普通的%字符,需要用两个连续的%来转义。
举例:

#include <stdio.h>

int main() {
    printf("The percentage is: %%\n");
    return 0;
}

输出结果为:

The percentage is: %

头文件math.h的常用函数

math.h是C语言标准库中的头文件,它提供了许多与数学相关的函数和常量。以下是一些常用的math.h函数和常量:

函数:

double sqrt(double x):计算平方根。

double pow(double x, double y):计算x的y次方。

double exp(double x):计算e的x次方。

double log(double x):计算x的自然对数。

double log10(double x):计算x的以10为底的对数。

double sin(double x):计算x的正弦值。

double cos(double x):计算x的余弦值。

double tan(double x):计算x的正切值。

double atan(double x):计算x的反正切值。

double fabs(double x):计算x的绝对值。

常量:

M_PI:圆周率π的近似值。

M_E:自然对数的底e的近似值。

这只是math.h中的一小部分函数和常量,还有许多其他的数学函数和常量可用于更复杂的计算。要使用math.h中的函数和常量,只需包含头文件#include <math.h>,然后使用相应的函数和常量即可。

头文件stdlib.h的常用函数

stdlib.h是C语言标准库中的头文件,它提供了一些常用的函数和类型,用于处理内存分配、随机数生成、字符串转换等操作。以下是一些常用的stdlib.h函数和类型:

函数:

void *malloc(size_t size):动态分配指定大小的内存空间。

void free(void *ptr):释放之前动态分配的内存空间。

void *calloc(size_t num, size_t size):动态分配指定数量和大小的内存空间,并将其初始化为0。

void *realloc(void *ptr, size_t size):重新分配之前动态分配的内存空间的大小。

int rand(void):生成一个伪随机数。

void srand(unsigned int seed):设置随机数种子。

int atoi(const char *str):将字符串转换为整数。

double atof(const char *str):将字符串转换为浮点数。

类型:

size_t:无符号整数类型,用于表示内存大小和数组索引。

NULL:表示空指针的常量。

这只是stdlib.h中的一小部分函数和类型,还有许多其他的函数和类型可用于不同的操作。要使用stdlib.h中的函数和类型,只需包含头文件#include <stdlib.h>,然后使用相应的函数和类型即可。

头文件string.h的常用函数

在C语言中,string.h是一个常用的标准库头文件,提供了一些字符串操作的函数和类型。以下是一些常用的string.h函数和类型:

函数:

char *strcpy(char *dest, const char *src):将一个字符串复制到另一个字符串。

char *strncpy(char *dest, const char *src, size_t n):将指定长度的字符串复制到另一个字符串。

char *strcat(char *dest, const char *src):将一个字符串拼接到另一个字符串的末尾。

char *strncat(char *dest, const char *src, size_t n):将指定长度的字符串拼接到另一个字符串的末尾。

int strcmp(const char *str1, const char *str2):比较两个字符串的大小。

int strncmp(const char *str1, const char *str2, size_t n):比较两个指定长度的字符串的大小。

size_t strlen(const char *str):返回一个字符串的长度。

char *strchr(const char *str, int c):在一个字符串中查找指定字符的第一个出现位置。

char *strstr(const char *haystack, const char *needle):在一个字符串中查找另一个字符串的第一次出现位置。

类型:

char:字符类型,用于表示单个字符。
这只是string.h中的一小部分函数和类型,还有许多其他的函数和类型可用于不同的字符串操作。要使用string.h中的函数和类型,只需包含头文件#include <string.h>,然后使用相应的函数和类型即可。

goto控制语句

在C语言中,goto是一种控制语句,用于无条件地将程序的控制转移到指定的标签位置。goto语句可以用来跳转到程序中的任何位置,但滥用goto语句可能导致程序难以理解和维护,因此需要谨慎使用。

下面是一个示例,展示了goto语句的基本用法:

#include <stdio.h>

int main() {
    int number;

    printf("请输入一个整数: ");
    scanf("%d", &number);

    if (number >= 0) {
        goto positive;
    } else {
        goto negative;
    }

positive:
    printf("这是一个正数\n");
    goto end;

negative:
    printf("这是一个负数\n");
    goto end;

end:
    printf("程序结束\n");

    return 0;
}

在这个示例中,用户输入一个整数,根据输入的值,程序通过goto语句跳转到对应的标签位置。如果输入的整数大于等于0,则跳转到positive标签处,打印"这是一个正数"。如果输入的整数小于0,则跳转到negative标签处,打印"这是一个负数"。最后,程序跳转到end标签处,打印"程序结束"。

需要注意的是,在实际的程序开发中,应尽量避免使用goto语句,而是使用结构化的控制语句(如if-else、while、for等)来实现程序的流程控制。这样可以使程序逻辑更加清晰、可读性更高。

malloc函数

在C语言中,malloc函数用于动态分配内存空间。它允许在程序运行时根据需要动态地分配指定大小的内存块,并返回一个指向该内存块的指针。

malloc函数的原型如下:

void *malloc(size_t size);

其中,size参数表示要分配的内存块的大小(以字节为单位)。malloc函数返回一个void类型的指针,指向分配的内存块的起始地址。如果内存分配失败,则返回NULL指针。

下面是一个示例,演示了如何使用malloc函数动态分配内存空间:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *numbers; // 定义一个指向整数的指针变量
    int size;

    printf("请输入要分配的整数个数: ");
    scanf("%d", &size);

    // 使用malloc动态分配内存空间
    numbers = (int *)malloc(size * sizeof(int));

    if (numbers == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    printf("请输入%d个整数:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &numbers[i]);
    }

    printf("输入的整数为: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    // 释放动态分配的内存空间
    free(numbers);

    return 0;
}

在这个示例中,程序首先定义了一个指向整数的指针变量numbers。然后,用户输入要分配的整数个数size。接下来,使用malloc函数动态分配size个整数的内存空间,并将返回的指针赋值给numbers变量。如果内存分配失败,则输出错误信息并退出程序。

程序继续让用户输入size个整数,并将它们存储在动态分配的内存空间中。最后,程序打印输入的整数,并使用free函数释放动态分配的内存空间,以防止内存泄漏。

需要注意的是,动态分配的内存空间在使用完毕后应该及时释放,以避免内存泄漏。通过调用free函数来释放动态分配的内存空间。

int *p;
p=(int *)malloc(sizeof(int));

需要注意,malloc函数的返回类型是void * 类型。void * 表示未确定类型的指针。在C和C++中,void * 类型可以强制转换为任何其他类型的指针。上面的代码中我们可以将其强制转化为整型指针,以便告诉计算机这里的4个字节作为一个整体用来存放整数。

free函数

在C语言中,free函数用于释放通过malloc、calloc或realloc等函数动态分配的内存空间。它的作用是将之前分配的内存空间标记为可重用,以便其他部分的程序可以再次使用该内存。

free函数的原型如下:

void free(void *ptr);

其中,ptr是一个指向先前通过malloc、calloc或realloc函数分配的内存块的指针。调用free函数后,ptr指向的内存块将被释放,并可以再次用于动态分配。

下面是一个示例,演示了如何使用malloc和free函数动态分配和释放内存空间:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *numbers;
    int size;

    printf("请输入要分配的整数个数: ");
    scanf("%d", &size);

    numbers = (int *)malloc(size * sizeof(int));

    if (numbers == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    printf("请输入%d个整数:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &numbers[i]);
    }

    printf("输入的整数为: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    // 释放动态分配的内存空间
    free(numbers);

    return 0;
}

在这个示例中,程序使用malloc函数动态分配一个包含size个整数的内存空间。然后,通过循环让用户输入这些整数,并将它们存储在动态分配的内存空间中。最后,程序打印输入的整数。

最后,通过调用free函数释放动态分配的内存空间。使用free函数后,numbers指向的内存块将被释放,可以再次用于动态分配。注意,释放后应避免继续使用该内存块,否则可能导致错误发生。

需要注意的是,只能释放通过malloc、calloc或realloc等函数动态分配的内存空间。调用free函数之前,确保已经完成了对该内存空间的使用,并且不再需要它。同时,不要尝试释放已经释放过的内存空间,这可能导致未定义的行为。

NULL和0是一个概念吗?

在C语言中,NULL和0在某些情况下可以互换使用,但它们不是完全相同的。

NULL是一个宏定义,用来表示一个指针不指向任何有效的内存地址。它在标准库的头文件stddef.h中有定义,通常是通过#define NULL ((void *)0)这样的形式来定义的。

而0是一个整型字面量,表示数值0。在一些上下文中,0可以被隐式转换为指针类型,例如将0赋值给指针变量、将0作为函数的返回值等。

虽然在某些情况下可以将NULL和0互换使用,但从语义上来说,NULL更适合表示指针不指向任何有效的内存地址,而0更适合表示数值0。在编程中,我们应该根据语义的需要合理选择使用NULL或0。
一个明显的例子是在条件语句中的判断。

例如

假设我们有一个指针变量int *ptr,我们想判断该指针是否指向有效的内存地址。

如果我们使用NULL来表示一个无效的指针,我们可以这样判断:

if (ptr == NULL) {
    printf("指针为空\n");
} else {
    printf("指针不为空\n");
}

在上面的代码中,如果ptr为NULL,则会输出"指针为空"。

但是,如果我们使用0来表示一个无效的指针,我们可以这样判断:

if (ptr == 0) {
    printf("指针为空\n");
} else {
    printf("指针不为空\n");
}

在这个例子中,0被隐式转换为一个指针类型,所以0和NULL在这里是等价的。但是,从代码的可读性和语义上来说,使用NULL更能表达我们的意图,即判断指针是否为空。

因此,在条件语句中判断指针是否为空时,使用NULL比使用0更加合适。

什么是隐式转换?

隐式转换(Implicit Conversion)是指在编程语言中,当出现不同数据类型之间的操作或赋值时,编译器自动进行的类型转换的过程。

在隐式转换中,编译器会根据一定的规则将一个数据类型转换为另一个数据类型,以使得操作或赋值得以顺利进行。这种自动转换的过程是在编译时进行的,程序员无需显式地进行类型转换的操作。

以下是一些常见的隐式转换情况:

1.整数提升(Integer Promotion):当进行低精度整数类型的运算时,编译器会将其提升为更高精度的整数类型,以保证运算的正确性。例如,将一个short类型的变量与一个int类型的变量相加时,编译器会将short类型提升为int类型,再进行相加。

2.隐式类型转换(Implicit Type Conversion):当进行不同类型的操作或赋值时,编译器会根据一定的规则进行类型转换。例如,将一个float类型的变量赋值给double类型的变量时,编译器会将float类型隐式地转换为double类型。

3.数组和指针转换(Array-to-Pointer Conversion):当使用数组名作为指针使用时,编译器会将数组名隐式地转换为指向数组第一个元素的指针。

需要注意的是,隐式转换虽然方便了编程过程,但也可能带来一些潜在的问题。因此,在编写代码时,应该注意隐式转换可能引发的类型错误,以确保程序的正确性和可读性。

const

在C语言中,关键字"const"用于定义常量。常量是指在程序执行过程中,其值不可被修改的变量。使用"const"关键字声明的变量,一旦被赋值,其值就不能再被改变。

常量的声明形式为:const 数据类型 常量名 = 值;

常量可以是各种数据类型,包括整型、浮点型、字符型等。例如:

const int MAX_VALUE = 100; // 定义一个整型常量MAX_VALUE,值为100
const float PI = 3.14; // 定义一个浮点型常量PI,值为3.14
const char LETTER = 'A'; // 定义一个字符型常量LETTER,值为'A'

常量的作用是为了提高程序的可读性和可维护性。通过将某些值声明为常量,可以避免在代码中直接使用魔法数(magic number),提高代码的可读性。同时,如果程序中的某个值需要在多个地方使用,并且希望保持一致,可以将其定义为常量,以免出现错误。

需要注意的是,const修饰的常量在编译时会被直接替换为字面值,而不会在运行时分配内存。因此,const常量的值在编译时就已经确定,无法再被修改。如果试图修改const常量的值,编译器会报错。

另外,const常量可以用于函数的参数和返回值。通过将参数声明为const,可以确保函数不会修改该参数的值。同样地,通过将函数的返回值声明为const,可以确保函数返回的值不会被修改。

总结起来,const关键字用于定义常量,可以提高代码的可读性和可维护性,同时也可以用于函数参数和返回值,确保数据的不可修改性。

const关键字也可以用于修饰指针。在C语言中,有两种使用const修饰指针的方式:

const修饰指针变量:const修饰的是指针变量本身,表示该指针变量不可以修改指向的地址,但可以修改指针指向的地址上的值。例如:

int value = 10;
const int* p = &value; // p是一个指向int类型常量的指针,不可以通过p修改value的值

const修饰指针指向的值:const修饰的是指针所指向的值,表示该指针所指向的值不可以修改。例如:

int value = 10;
int* const p = &value; // p是一个指向int类型的指针常量,不可以通过p修改指向的地址,但可以通过*p修改value的值

需要注意的是,const修饰的指针变量本身可以修改指向的地址,但是不能通过该指针变量来修改指向的值;而const修饰的指针所指向的值不可以修改,但是可以通过其他方式修改指向的地址。

使用const修饰指针的好处是可以提高代码的安全性和可读性。通过将指针声明为const,可以明确指出该指针不会修改所指向的值,从而避免在程序中意外修改了不应该修改的数据。同时,它也可以作为函数参数的声明,确保函数不会通过指针修改传入的数据。

放大招啦!!!放大招啦!!!放大招啦!!!下面是大招!!!

const修饰指针变量的例子:

int value = 10;
const int* p = &value; // p是一个指向int类型常量的指针,不可以通过p修改value的值
// *p = 20;  // 错误,不可以通过p修改value的值
value = 20; // 可以直接修改value的值

const修饰指针指向的值的例子:

int value = 10;
int* const p = &value; // p是一个指向int类型的指针常量,不可以通过p修改指向的地址
// p = &newValue;  // 错误,不可以修改p指向的地址
*p = 20; // 可以通过*p修改value的值

const修饰常量的例子:

const int MAX_VALUE = 100; // MAX_VALUE是一个常量,不可以修改其值
// MAX_VALUE = 200; // 错误,不可以修改常量的值

const int* p = &MAX_VALUE; // p是一个指向int类型常量的指针,不可以通过p修改MAX_VALUE的值
// *p = 200; // 错误,不可以通过p修改MAX_VALUE的值

int* const p = &MAX_VALUE; // 错误,不可以将常量的地址赋给指针常量

这些例子展示了不同情况下const修饰指针和常量的用法和限制。在实际编程中,根据需要选择合适的方式来使用const修饰指针和常量,以保证代码的安全性和可读性。

const同时修饰指针和常量:

const int value = 10;
const int* const p = &value; // p是一个指向int类型常量的指针常量,不可以修改指针指向的地址和指向的值
// p = &newValue; // 错误,不可以修改p指向的地址
// *p = 20; // 错误,不可以通过p修改指向的值

在上面的例子中,常量value被声明为不可修改的常量,而指针p被声明为既不可修改指向的地址,也不可修改指向的值。这意味着无法通过p修改value的值,也无法修改p指向的地址。

C 共用体 union

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

定义共用体
为了定义共用体,您必须使用 union 语句,方式与定义结构类似。union 语句定义了一个新的数据类型,带有多个成员。union 语句的格式如下:

union [union tag]
{
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];

union tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在共用体定义的末尾,最后一个分号之前,您可以指定一个或多个共用体变量,这是可选的。下面定义一个名为 Data 的共用体类型,有三个成员 i、f 和 str:

union Data
{
   int i;
   float f;
   char  str[20];
} data;

现在,Data 类型的变量可以存储一个整数、一个浮点数,或者一个字符串。这意味着一个变量(相同的内存位置)可以存储多个多种类型的数据。您可以根据需要在一个共用体内使用任何内置的或者用户自定义的数据类型。

共用体占用的内存应足够存储共用体中最大的成员。例如,在上面的实例中,Data 将占用 20 个字节的内存空间,因为在各个成员中,字符串所占用的空间是最大的。下面的实例将显示上面的共用体占用的总内存大小:

实例

#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   printf( "Memory size occupied by data : %d\n", sizeof(data));
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Memory size occupied by data : 20

访问共用体成员
为了访问共用体的成员,我们使用成员访问运算符(.)。成员访问运算符是共用体变量名称和我们要访问的共用体成员之间的一个句号。您可以使用 union 关键字来定义共用体类型的变量。下面的实例演示了共用体的用法:

实例

#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   data.i = 10;
   data.f = 220.5;
   strcpy( data.str, "C Programming");
 
   printf( "data.i : %d\n", data.i);
   printf( "data.f : %f\n", data.f);
   printf( "data.str : %s\n", data.str);
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming

在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。现在让我们再来看一个相同的实例,这次我们在同一时间只使用一个变量,这也演示了使用共用体的主要目的:

实例

#include <stdio.h>
#include <string.h>
 
union Data
{
   int i;
   float f;
   char  str[20];
};
 
int main( )
{
   union Data data;        
 
   data.i = 10;
   printf( "data.i : %d\n", data.i);
   
   data.f = 220.5;
   printf( "data.f : %f\n", data.f);
   
   strcpy( data.str, "C Programming");
   printf( "data.str : %s\n", data.str);
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

data.i : 10
data.f : 220.500000
data.str : C Programming

在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。

注意!!!!!!!!!!!!!!!!!!!!!!!!!!!
tips:

① 结构体变量所占内存长度是其中最大字段大小的整数倍(参考:结构体大小的计算)。

共用体变量所占的内存长度等于最长的成员变量的长度。例如,教程中定义的共用体Data各占20个字节(因为char str[20]变量占20个字节),而不是各占4+4+20=28个字节。

union Data
{
   int i;
   float f;
   char  str[20];
} data;  

② union Data{
int i;
float f;
char str[9];
double d;
}data;
共用体所占内存并非是9个char,即9个字节,而是double的两倍,即16个字节

③ 而且每次输出都是它前面离它最近的值

strncpy的作用

在C语言中,strncpy是一个用于复制字符串的函数。其原型为:

char *strncpy(char *dest, const char *src, size_t n);

函数接受三个参数:

dest:指向目标字符串的指针,表示复制后的字符串将被存储的位置。
src:指向源字符串的指针,表示要复制的字符串。
n:要复制的字符数。

strncpy函数将源字符串src的前n个字符复制到目标字符串dest中,并返回dest。如果源字符串的长度小于n,则目标字符串将被填充到n个字符,并以空字符’\0’结束。如果源字符串的长度大于等于n,则目标字符串不会以空字符’\0’结束,因此需要在dest的后面手动添加’\0’。

需要注意的是,strncpy函数并不会自动添加’\0’,这意味着如果目标字符串的长度小于n,则目标字符串中可能不会以’\0’结束,这可能会导致一些问题。因此,在使用strncpy时,需要特别小心处理字符串的结束符。

strncpy函数在C语言中的头文件是string.h。因此,如果要使用strncpy函数,需要包含以下头文件:

#include <string.h>

clock()函数

定义:捕捉从程序开始运行到clock()被调用时所耗费的时间。这个时间单位是clock tick,即“时钟打点”。
常数 CLK_TCK:机器时钟每秒所走的时钟打点数

#include<stdio.h>
#include<time.h>

clock_t start, stop;
/*  clock_t是clock()函数返回的变量类型  */
double duration;
/*  记录被测函数运行时间,以秒为单位  */
int main()
{
	/*  不在测试范围捏的准备工作写在clock()调用之前  */
	start = clock();	/*  开始计时  */
	myfunction();		/*  把被测函数加在这里  */
	stop = clock();		/*  停止计时  */
	duration = ((double)(stop - start)) / CLK_TCK;
	/*  计算运行时间  */
	/*  其他不在测试范围的处理写在后面,例如输出duration的值  */
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值