C语言基础知识笔记——万字学习记录

news2024/11/23 2:54:33

Hi,大家好,我是半亩花海。本文主要参考浙大翁恺老师的C语言讲解以及其他博主的C语言学习笔记,进而梳理C语言的基础知识,为后续系统性学习数据结构其他语言等知识夯实一定的基础。(其他博主学习笔记的链接包括:C语言入门基础知识【完整版】_c语言基础知识入门-CSDN博客和C语言基础知识入门大全-CSDN博客以及C语言初阶——手把手教零基础/新手入门(万字心得笔记)_c语言入门自学零基础-CSDN博客)


目录

一、数据类型和运算表达式

1. 数据类型

(1)基本数据类型

(2)构造数据类型

(3)指针类型

(4)空类型

2. 常量

3. 运算表达式

(1)算术运算表达式

(2)关系运算表达式

(3)逻辑运算符

(4)位运算符

二、语句

1. 条件判断语句

(1)简单if语句

(2)if-else语句

(3)多重if-else语句

(4)嵌套if-else语句

(5)switch语句

2. 循环执行语句

(1)while循环

(2)do-while循环

(3)for循环

3. 转向语句

三、函数

1. 函数的定义

2. 函数的参数

3. 全局变量与局部变量

(1)全局变量

(2)局部变量

4. 静态变量与寄存器变量

(1)静态变量

(2)寄存器变量

5. 预处理命令

(1)文件包含

(2)宏定义

6. 常用函数

四、指针

1. 变量的指针和指针变量

(1)变量的指针

(2)指针变量

2. 数组的指针和指针数组

(1)数组的指针

(2)指针数组

3. 字符串的指针

4. 函数的指针&指针型函数

(1)函数的指针

(2)指针型函数

5. 指向指针的指针

五、结构体和公用体

1. 结构体

(1)结构体的定义

(2)结构体的用法

2. 共用体

(1)共用体的定义

(2)共用体的用法

六、动态内存分配

以 malloc() 函数为例

附录1:C语言常见头文件

附录2:文件操作

1. 文件指针

2. 操作文件的函数

(1)打开与关闭

(2)文件读写

(3)文件操作实例


一、数据类型和运算表达式

C语言中二进制数、八进制数和十六进制数的表示:

  • 二进制:二进制由 0 和 1 两个数字组成,使用时必须以 0b 或 0B(不区分大小写)开头。例如:0b101(十进制的 5)、0B111(十进制的 7)。(注意:标准的C语言并不支持二进制写法,有些编译器自己进行了扩展,才会支持二进制数字
  • 八进制:八进制由 0~7 八个数字组成,使用时必须以数字 0 开头。例如:021(十进制的 17)、01010(十进制的 520)。
  • 十六进制:十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,使用时必须以0x或0X(不区分大小写)开头。例如:0X2A(十进制的43)、0xffff(十进制的65535)。

1. 数据类型

(1)基本数据类型

  • 整型
  • 字符型
  • 实型(浮点型):单精度(float)和双精度(double)
类型类型说明符字节
字符型char1 Byte
基本整型int4 Byte
短整型short int2 Byte
长整型long int4 Byte
无符号整型unsigned int4 Byte
无符号长整型unsigned long4 Byte
单精度实型float4 Byte
双精度实型double8 Byte

注意:

  • short int 和 long int 是根据编译环境的不同,所取范围不同。
  • C语言 int 的取值范围在于他占用的字节数 ,不同编译器规定不一样(16位占2字节,32位或64位占4字节)。

(2)构造数据类型

  • 枚举类型
  • 数组类型
  • 结构体类型
  • 共用体类型

(3)指针类型

(4)空类型

2. 常量

C语言中常量的定义有两种方式,假如我们要定义一个int类型、名为TEMP、值为1的常量:

  • 预定义命令(#define 定义的标识符常量):
    #define TEMP = 1
  • const关键字(const 修饰的常变量):
    const int TEMP = 1

3. 运算表达式

(1)算术运算表达式

  • 加:+
  • 减:-
  • 乘:*
  • 除:/
  • 取余:%
  • 自增:++
  • 自减:--

注意:

a++和++a(a--和--a)有区别,解释如下:

表达式运算表达式的值
a++给a加1a++为a原来的值
++a给a加1a++为a+1以后的值
a--给a减1a--为a原来的值
--a给a减1a--为a-1以后的值
#include <stdio.h>

int main(){
	int a = 10;
	
	printf("a++=%d\n", a++); //a原来的值
	printf("a=%d\n", a); //a+1后的值
	
	printf("++a=%d\n", ++a); //上述a+1后的值
	printf("a=%d\n", a); //与++a的值一样
	
	return 0;
}

(2)关系运算表达式

  • 等于:==
  • 大于:>
  • 大于等于:>=
  • 小于:<
  • 小于等于:<=
  • 不等于:!=

(3)逻辑运算符

C语言中 0 表示假,1 表示真。

  • 与:&&
  • 或:||
  • 非:!

(4)位运算符

  • 位与:&(对每一位进行逻辑与运算,0表示假,1表示真:0011 & 1111 = 0011)
  • 位或:|(对每一位进行逻辑或运算,0表示假,1表示真:0011 | 1111 =1111)
  • 位非:~(对每一位进行逻辑非运算,0表示假,1表示真:~1111 =0000)
  • 位异或:^(对每一位进行逻辑异或运算,0表示假,1表示真:0011 ^ 1111 =1100)
  • 左移:<<(高位溢出丢弃,低位不足补0:01100100 << 2 = 10010000)
  • 右移:>>
    • 正数:高位补0,低位溢出舍去:01111111 >> 4 = 00000111
    • 负数:高位补1,低位溢出舍去:11111111 >> 4 = 11111111

二、语句

1. 条件判断语句

(1)简单if语句

如果表达式的值为真,则执行其后的语句,否则不执行该语句。 

if(表达式){
    执行代码块;
}

(2)if-else语句

如果表达式的值为真,则执行代码块1,否则执行代码块2。

if(表达式){
    执行代码块1;
}
else{
    执行代码块2;
}

(3)多重if-else语句

if(表达式a){
    执行代码块a;
}
...
else if(表达式b){
    执行代码块b;
}
...
else{
    执行代码块c;
}

(4)嵌套if-else语句

if(表达式a){
    if(表达式b){
        执行代码块;
    }
    else{
        执行代码块;
    }
}
else{
    执行代码块;
}

(5)switch语句

如果表达式的值等于常量1,执行下面的语句1;如果没有通过上面的开关语句退出,就会执行下面的语句n+1。(default可以省略break,因为它本身就是最后执行,执行完就会退出开关语句)。

注意:

switch语句如果没有break一直向下执行直到结束。 

switch(表达式){
	case 常量1: 
		语句1;
		break;

	case 常量2: 
		语句2;
		break;

	case 常量a:
		语句a;
		break;

	default:
		语句b;
		break; //break可以忽略
}

2. 循环执行语句

(1)while循环

计算表达式的值,当值为真(非0)时, 则执行循环体代码块。

while(循环条件){
	执行代码块;
}

举个例子:

int main(){
    int n = 0;
    while(n <= 5) //条件判断
    {
	    printf("a=%d\n", n); //花括号内为语句块
        n++; //计数器放最后
    }
    return 0;
}

注意:

  • 一定要记着在循环体中改变循环变量的值,否则会出现死循环(无休止的执行)。
  • 循环体如果包括有一个以上的语句,则必须用{}括起来,组成复合语句。

(2)do-while循环

它先执行循环中的执行代码块,然后再判断while中表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while循环至少要执行一次循环语句。

do{
	执行代码块;
}while(循环条件);

举个例子:

int i = 0;
do{
   i++;
}while(...);

注意:使用do-while结构语句时,while括号后必须有分号。

(3)for循环

for(循环变量赋初值;循环条件;循环变量增量){
	执行代码块;
}

举个例子:

int main(){
    int i = 0;
    for(i = 0; i <= 5; i++){
	    printf("a=%d\n", i);
    }
    return 0;
}

3. 转向语句

(1)break语句

中断语句,一般用于循环结构中,作用是终止循环,当执行到break语句时,会立即退出循环。

(2)continue语句

continue语句一般用于循环结构中,作用是跳过当次循环,当循环语句执行到continue时,不会继续向下执行,会跳过当次循环,直接执行下一次循环

(3)return语句

跳出函数语句,用于跳出函数返回一个值


三、函数

1. 函数的定义

函数是实现了某种功能的代码块。函数的定义方式主要分为无参函数和有参函数。

//无参函数
类型标识符 函数名(){
	声明部分;
	语句;
}

//有参函数
类型标识符 函数名(形参1,形参2,形参3...形参n){
	声明部分;
	语句;
}

示例:下面定义了两个函数,第一个 HelloWorld 是无参函数,功能是输出一个 "Hello World!" 字符串,第二个 FindMax 是有参函数,接收两个 int 类型的参数,返回两个数中最大的那个数。

//函数的调用必须先定义声明,否则编译不通过
void HelloWorld(){ //返回类型为void,表示不返回任何值。
    printf("Hello World!");
}

int FindMax(int a, int b){
    int max; //声明一个整数变量max,用于存储比较后的最大值。
    max = a >= b ? a : b; //使用条件运算符(三元运算符)比较a和b大小并将大值赋给max。
    return max; //返回类型为int,表示返回一个整数值。
}

int main(){
	HelloWorld(); //调用HelloWorld函数,会打印输出"Hello World!"。
    int a = 5; //声明一个整数变量a,并赋值为5。
    int b = 10; //声明一个整数变量a,并赋值为10。
    int c; //声明一个整数变量c,用于存储最大值。
    c = FindMax(a, b); //调用FindMax函数,传入a和b作为参数,将返回值赋给变量c。
    printf("\n最大数为:%d\n", c); //使用%d占位符表示整数
    return 0; //返回0,表示程序正常运行结束。
}

注意:

1)void是一种特殊的数据类型,表示“无类型”或“空类型”。当一个函数的返回类型是void时,该函数执行完毕后不会返回任何结果,或者说返回值为空

2)三元运算符的语法结构如下condition ? expression1 : expression2;

语义如下:如果条件为真(即condition为真),则返回expression1的值;如果条件为假(即condition为假),则返回expression2的值。

2. 函数的参数

  • 形参:形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。
  • 实参:实参在主调函数中,是调用函数时传递的参数。
  • 参数传递:函数的参数由主调函数的实参传递给被调函数的形参,因此实参与形参的顺序、类型必须保持一致。

3. 全局变量与局部变量

(1)全局变量

全局变量也称为外部变量,它是在函数外部定义的变量

在全局变量定义之前的函数中使用全局变量,需要使用关键字 extern做全局变量说明,声明某个变量是全局变量,然后才能使用

int a = 5; //此处a为全局变量

int main(void){
	int extern a; //全局变量说明,声明a是一个全局变量,此处在a定义之后,可以省略该说明
	printf("%d", a); //输出结果为5
}

(2)局部变量

局部变量也称为内部变量。局部变量是函数内部定义的变量作用域仅限于函数内部,局部变量只能在函数内部使用,函数外部无法访问

int main(void){
	int a = 5; //这是一个局部变量,a的作用域范围是main函数内,在函数外无法使用
	print("%d", a);
	a++;
}
print("%d", a); //全局作用域内找不到变量a,编译不通过

4. 静态变量与寄存器变量

(1)静态变量

静态变量是在函数调用结束后不消失而保留原值的变量,如果在一个函数调用结束后,希望它保留某个变量的值,就把这个变量static关键字声明为静态变量

//定义一个自增函数,初始化局部静态变量a=0,每调用一次,a自增1
int Add(){
    static int a = 0;
    a++;
    return a;
}

int main(){
	print("%d", Add()); //输出结果为1
	print("%d", Add()); //输出结果为2
	return 0;
}

(2)寄存器变量

寄存器变量是在CPU寄存器中的变量,CPU寄存器可以理解为CPU的内存空间,就像是电脑的内存一样,在寄存器中运算速度非常快,使用 register关键字声明

注意:

  • 只有局部自动变量(非静态变量)和形参可以作为寄存器变量,局部静态变量不能定义为寄存器变量。
  • 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量。
#include "stdio.h"

//这是一个计算n的阶乘的函数,将局部变量i和f声明为寄存器变量
int fac(int n){
    register int i, f = 1;
    for (i = 1; i <= n; i++){
        f = f * i;
    }
    return f;
}

int main(){
    int i;
    for (i = 0; i <= 5; i++){ 
        printf("%d!=%d\n", i, fac(i)); 
    }
    return 0;
}

5. 预处理命令

(1)文件包含

文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件(头文件)

文件包含的形式为:#include "文件名"或 #include <文件名>

C语言常见头文件详见附录1

(2)宏定义

C语言可以使用 #define定义宏(类似常量),程序在编译处理时会把源程序中所有的宏名替换成宏定义的结果。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。

  • 无参宏定义:#define 标识符 字符串 (“字符串”可以是常数、表达式、格式串等)
#include <stdio.h>
#define PI 3.1415926
#define M (a+a)

int main(void){
  double a = 1.0;
   double b;
   b = 2*M + PI; //等同于2*(a+a) + 3.1415926
   printf("%f", b);
   return 0;
}
  • 带参宏定义:#define 宏名(形参1,形参2,形参3,...形参n) 字符串 (“字符串”可以是常数、表达式、格式串等)
#include <stdio.h>
#define S(x,y) x*y //S表示矩形面积,x,y分别表示长宽

int main(void){
  double a = 3.0,b = 4.0;
   double s;
   s = S(a,b); //等同于a*b
   printf("%f", s);
   return 0;
}

6. 常用函数

C语言中常用的函数有很多,以下是一些常见的函数:

  • printf():用于输出数据到标准输出设备(通常是控制台),输出完结果后不会自动换行。
  • scanf():用于从标准输入设备(通常是键盘)读取数据
  • putchar():用于输出字符,每次只能输出一个字符,若想输出多个字符则需使用多个putchar()函数,输出完结果后不会自动换行。
  • getchar():用于获取字符,getchar()函数没有参数,且只能接收一个字符,若想输入多个字符则需使用多个getchar()函数。
  • malloc():用于动态分配内存
  • free():用于释放动态分配的内存
  • strcpy():用于复制字符串。
  • strlen():用于获取字符串的长度。
  • strcat():用于将一个字符串追加到另一个字符串的末尾。
  • strcmp():用于比较两个字符串。
  • fopen():用于打开文件
  • fclose():用于关闭文件
  • fprintf():用于将数据输出到文件。
  • fscanf():用于从文件中读取数据。
  • abs():用于计算整数的绝对值。
  • pow():用于计算一个数的指数幂。
  • sqrt():用于计算一个数的平方根。

这些是C语言中常用的函数,还有很多其他函数可以根据具体需求使用。

例如,scanf() 和 printf() 函数如下所示。

# include <stdio.h>

int main(void){
	int a, b;
	
	printf("请输入两个整数:");
	scanf("%d %d", &a, &b); //&a, &b表示变量a, b的地址,&是取地址符
	printf("%d + %d = %d\n", a, b, a + b);
	
	return 0;
}

结果如下:

再例如,putchar() 和 getchar() 函数如下所示。

#include <stdio.h>

int main()
{
    char a = 'A', b = 'W', c = 'M';
    int d = 97, e = 99, f = 101;
    
    putchar(a); //输出字符常量
    putchar(b);
    putchar(c);
    putchar(d); //输出整型常量,通过ASCII码值将int类型的数值转换为字符
    putchar(e);
    putchar(f);
    putchar('\n'); //putchar()函数不能自动换行
    
    putchar('\101'); //输出字符A
    putchar('\''); //输出单撇号,括号中的 \' 是转义字符,代表单撇号',
    putchar('\015'); //八进制数15等于十进制数13,13的ASCII码值表示的是回车
}

结果如下:

#include <stdio.h>

int main()
{
    char a, b, c;
    
    a = getchar();
    b = getchar();
    c = getchar();
    
    putchar(a);
    putchar(b);
    putchar(c);
    printf("\n");
}

结果如下:

注意:

在 printf 中,所有的“非输出控制符”都要原样输出。而在 scanf 中,所有的“非输入控制符”都要原样输入。如下面的结果图所示,在输入的时候,“a=”和“b=”必须要原样输入按代码所示,%d和%d之间也需输入一个空格,且不能使用逗号或其他符号代替,否则报错

另外,在 scanf 函数中%d后面也没有必要加\n,因为在 scanf 中\n不起换行的作用;在 printf 函数中 \n 能起到换行的作用,否则就会将输出体现在同一行里。


四、指针

指针是指存储单元的地址,例如定义一个变量int a = 1;指向变量a的指针就是a在内存中的地址

1. 变量的指针和指针变量

(1)变量的指针

变量的指针:就是变量的内存地址

(2)指针变量

  • 指针变量的定义类型说明符* 变量名

指针变量:存放变量地址的变量。(例如:有个变量a,变量a的地址是p,p就是变量a的指针。现在我们再假设一个变量b,然后把p赋值给变量b,那么变量b就是一个指针变量。)

int* a; //定义一个int类型的指针变量,指向的变量类型也必须是int
char* b; //定义一个char类型的指针变量,指向的变量类型也必须是char
double* c; //定义一个double类型的指针变量,指向的变量类型也必须是double
  • 指针的操作

1)&:取地址运算符

&变量名表示取变量的地址(指针)

int a = 123;
int* p = &a; //取变量a的地址赋值给指针变量p

2) *:指针运算符

*指针变量表示取指向的变量的

int a = 123;
int* p = &a; //取变量a的地址赋值给指针变量p
printf("%d",*p); //输出123,*p表示取a的值

2. 数组的指针和指针数组

(1)数组的指针

一个数组是由连续的一块内存单元组成的。数组的指针是指数组的这块连续内存单元的首地址,即数组中第一个元素的地址

int array[] = {1,2,3,4,5,6};
int* pA = array; //数组名就是数组的指针
int* pB = &array[0]; //数组的第一个元素的地址就是数组的指针
//指针pA和指针pB是相等的

(2)指针数组

一个数组的元素值为指针则是指针数组。

定义方式:类型说明符* 数组名[数组长度](跟普通数组定义方式相同,唯一区别是*)

int main(){
   int a=1,b=2,c=3,d=4,e=5;
   int* Int[5] = {&a,&b,&c,&d,&e}; // 这是一个整型指针数组
   // 字符串在C语言中是字符数组,所以一个字符串相当于一个字符数组,字符串本身就等于字符数组的指针(首地址)
   const char* String[] = {"Test1","Test2","Test3","Test4","Test5"}; // 这是一个字符型的指针数组
   for (int i = 0; i < 5; ++i){
       printf("%p\n",String[i]); // 这里输出的就是每个字符串的指针
   }
   return 0;

3. 字符串的指针

  • C语言中是没有字符串类型的,C语言中的字符串都是用字符数组进行存储
  • 字符串的指针就是字符数组的指针,也就是字符数组的首地址

C语言字符串的两种定义形式:

  • 数组形式:char string[] = {'H','e','l','l','o','\0'}; 或 char string[] = "Hello";
  • 指针形式:char* string = "Hello";(等价于{'H','e','l','l','o','\0'})

4. 函数的指针&指针型函数

(1)函数的指针

在C语言中一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址(即函数指针)。

  • 函数指针的定义:类型说明符 (*指针变量名)(实参类型);
int (*p)(); //定义一个函数指针p
int Function(){
    printf("test");
}
p = Function; //将Function函数的入口地址赋值给函数指针变量p

注意:函数指针的定义区别于变量指针。

  • 函数指针的调用:(*指针变量名) (实参表);
int FindMax(int a, int b){
    return a > b ? a : b;
}

int main(){
    int (*p)(int, int) = FindMax;
    int max = p(5,10);
    printf("%d",max);

    return 0;
} 

(2)指针型函数

函数类型(即函数返回值的类型)是指针的函数就是指针型函数

指针型函数的定义:

类型说明符* 函数名(参数){
   执行语句;
   return 对应类型的指针;
}

例如:下面定义了指针型函数,作用是随机生成一个数组,返回数组的指针。

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

int* GetNumber(){
    static int array[10];
    srand((unsigned)time(NULL));
    for (int i = 0; i < 10; ++i){
        array[i] = rand();
        printf("%d\n",array[i]);
    }
    return array;
}

int main(){
    int* p = GetNumber();
    printf("==================\n");
    for (int i = 0; i < 10; ++i){
        printf("%d\n",p[i]);
    }

5. 指向指针的指针

指向指针的指针,根据字面意思举个例子:假如有个变量a,变量a的指针用p1表示,将p1赋值给一个变量b,变量b的指针用p2表示,现在将p2赋值给一个变量c,变量c就是指向指针的指针。

int a = 2333;
int* b = &a;
int** c = &b;

要访问指向指针的指针的值,要使用**。如上面的指针c,访问方式为**c。


五、结构体和公用体

1. 结构体

结构体是由多个不同数据类型的成员组成的数据类型。结构体中的每个成员可以有不同的数据类型和命名。使用结构体可以多个不同数据类型的信息组合成一个单一的逻辑单元(实现面向对象编程),从而方便地进行操作

(1)结构体的定义

  • 定义结构体关键字struct
  • 定义形式struct 结构名{成员数据};
//下面定义了一个名为Person的结构体
//Person包含有一个人的姓名、年龄、性别、身高、住址信息
struct Person{
    char* name;
    int age;
    char sex;
    double height;
    char address[200];
};

(2)结构体的用法

  • 结构体成员变量表示方法结构名.变量名(*结构指针).变量名/(*结构指针)->变量名
  • 结构体变量的赋值直接给成员变量赋值,注意数组类型不能直接赋值
#include <stdio.h>
#include <string.h>

// 下面定义了一个名为Person的结构体,Person包含有一个人的姓名、年龄、性别、身高、住址信息
struct Person{
    char* name;
    int age;
    char sex;
    float height;
    char address[200];
};

int main(){
    struct Person man;
    struct Person woman;
    struct Person* pW = &woman;
    man.name = "小明"; // 结构体变量赋值
    man.sex = 'M';
    man.age = 18;
    man.height = 1.78f;
    strcpy(man.address,"四川省成都市");
    (*pW).name = "小红"; // 结构体变量赋值
    (*pW).sex = 'W';
    pW->age = 19;
    pW->height = 1.68f;
    strcpy(pW->address,"四川省绵阳市"); // 数组类型不能直接赋值

    printf("姓名:%s\n年龄:%d\n性别:%c\n身高:%.2fm\n地址:%s\n",man.name,man.age,man.sex,man.height,man.address);
    printf("姓名:%s\n年龄:%d\n性别:%c\n身高:%.2fm\n地址:%s\n",woman.name,woman.age,woman.sex,(*pW).height,pW->address);

    return 0;
}

2. 共用体

共用体是一种特殊的结构体,其所有成员共享相同的内存空间。共用体中的每个成员可以有不同的数据类型,但是它们共享相同的内存空间,因此只能同时存在一个成员的值

共用体的主要用途不同的数据类型之间进行类型转换节省内存空间

(1)共用体的定义

  • 定义结构体关键字union
  • 定义形式union 共用体名{成员数据};

(2)共用体的用法

#include <stdio.h>
#include <string.h>

union data{
    int i;
    float f;
    char* s;
    char c;
};

int main(){
    union data temp; // 定义一个共用体temp
    temp.i = 10;

    printf("temp = %d\n",temp.i);
    printf("data中i的内存地址:%p\n",&temp.i);
    printf("data中f的内存地址:%p\n",&temp.f);
    printf("data中s的内存地址:%p\n",&temp.s);
    printf("data中c的内存地址:%p\n",&temp.c);
    // 可以看出共用体的所有成员指向的是同一块内存空间
    printf("==================================\n");
    temp.s = "测试";
    printf("temp = %s\n",temp.s);
    printf("data中i的内存地址:%p\n",&temp.i);
    printf("data中f的内存地址:%p\n",&temp.f);
    printf("data中s的内存地址:%p\n",&temp.s);
    printf("data中c的内存地址:%p\n",&temp.c);
    printf("==================================\n");
    temp.f = 3.14159f;
    printf("temp = %f\n",temp.f);
    printf("data中i的内存地址:%p\n",&temp.i);
    printf("data中f的内存地址:%p\n",&temp.f);
    printf("data中s的内存地址:%p\n",&temp.s);
    printf("data中c的内存地址:%p\n",&temp.c);
    printf("==================================\n");

    return 0;
}

通过上面的例子,如果把temp看做一个没有定义类型的变量,那么他就是个可变类型的变量。


六、动态内存分配

C语言常用的内存管理函数有四个:malloccallocreallocfree。其中申请空间的函数是malloccalloc;重新调整空间大小的函数是realloc释放空间的函数是free

malloc() 函数为例

malloc 只开辟空间(开辟的是用户所需求的字节数大小的空间),且不进行类型检查,只是在使用的时候进行类型的强制转换(malloc() 函数返回的实际是一个无类型指针,必须在其前面加上指针类型强制转换才可以使用)。

malloc() 函数使用形式: 指针自身 = (指针类型 *)malloc(sizeof(指针类型) * 数据数量)

例如:分配一块空间存储指定个数的数字,并对数字求和

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

int main() {
    int* ptr; //定义一个指针变量
    int n, sum = 0; //初始化元素个数与元素总和

    printf("输入要保存的元素个数: ");
    scanf("%d", &n);

    ptr = (int*) malloc(n * sizeof(int)); //申请一片连续的存储空间,强制转换为int类型指针

    if(ptr == NULL) {
        printf("内存空间分配失败!\n");
        exit(1);
    }
    printf("输入保存的元素:\n");

    for(int i = 0; i < n; i++) {
        scanf("%d", &ptr[i]);
        sum += ptr[i];
    }
    printf("所有元素累加总和为:%d\n", sum);

    free(ptr); //释放内存空间ptr

    return 0;
}

附录1:C语言常见头文件


C语言中常用的头文件包括但不限于以下几个:

  1. <stdio.h>提供了输入输出函数(Sandard Input and Output Header,标准输入输出头文件),如printfscanffopen等。
  2. <stdlib.h>提供了常用的实用函数(Standard Library Header,标准库头文件),如内存分配函数mallocfree, 随机数生成函数rand等。
  3. <string.h>提供了字符串处理函数,如strlenstrcpystrcat等。
  4. <math.h>提供了数学函数库,如三角函数sincossqrt等。
  5. <time.h>提供了与时间有关的函数,如time,用于获取当前时间。
  6. <ctype.h>提供了字符处理函数,如tolowertoupper, 测试字符类别的函数isalpha等。
  7. <stdbool.h>提供了布尔类型booltruefalse
  8. <limits.h>提供了有关实现常量的信息,如INT_MAXINT_MIN等。

使用这些头文件通常只需在程序的开始部分加上相应的包含指令即可,例如:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <stdbool.h>
#include <limits.h>


附录2:文件操作

文件是一个有序数据集,数据集的名称叫文件名。文件分为两种,一种是普通文件,比如txt文件、C语言的源程序文件、头文件等等存在于磁盘上的;另一种是设备文件,比如鼠标、键盘、显示器等等外部设备,都认为是一个文件。

1. 文件指针

C语言使用一个指针变量指向一个文件通过操作指针来操作文件

文件指针的定义:FILE *变量名;(FILE 实际上是系统定义的一个结构体,该结构体中含有文件名、文件状态、文件当前位置等信息)

注意:

文件指针和文件位置指针需要区别开:文件指针指向的是整个文件,文件位置指针指的是文件中所处位置的指针(头部、当前位置、末尾等)。

2. 操作文件的函数

(1)打开与关闭

ANSIC 规定用 fopen 来打开文件,用 fclose 来关闭文件:

  • fopen:打开一个文件,成功则返回文件的指针,失败则返回空指针NULL
  • fclose:关闭一个文件,成功则返回0,失败则返回非0

函数原型如下:

FILE * fopen ( const char * filename, const char * mode );	//打开文件,fopen("文件名", "打开模式")
int fclose ( FILE * stream );	//关闭文件
  • path:文件路径。
  • mode:打开的模式。mode主要由以下6个字符组合而成:
    r:可读(文件位置指针在文件头部,文件必须存在)
    w:可写(文件位置指针在文件头部,文件存在则清空内容,不存在就创建)
    a:追加写入(文件位置指针在文件尾部,文件必须存在)
    b:二进制方式打开
    +:可读写
    t:文本模式(默认,可省略)

  • mode的常用模式:

    选项说明
    r只读打开一个文本文件,只允许读数据
    w只写打开一个文本文件,只允许写数据
    a追加写入打开一个文本文件,在文件末尾写数据
    rb以二进制方式打开一个文件,只允许读数据
    wb以二进制方式打开一个文件,只允许写数据

(2)文件读写

文件结束符:EOF

文件写入的函数需要以或者读写模式打开文件,文件读取的函数需要以或者读取的模式打开文件。读取或写入操作之后,位置指针都会向后移动到读取或写入位置的末尾

函数名功能适用性
fgetc()字符输入函数所有输入流
fputc()字符输出函数所有输出流
fgets()文本行输入函数所有输入流
fputs()文本行输出函数所有输出流
fscanf()格式化输入函数所有输入流
fprintf()格式化输出函数所有输出流
fread()二进制输入文件
fwrite()二进制输出文件

1)fgetc:从文件读取一个字符

  • 函数原型:int fgetc(FILE *file);
    • file:目标文件的指针
  • 返回值:返回 int 类型的 ASCII 码,位置指针向后移动一个字节
  • 使用方法:fgetc(文件指针);

2)fputc:向文件中写入一个字符

  • 函数原型:int fputc(int c, FILE *file);
    • c:要写入的字符(char 或者 int 类型 ASCII 码)
    • file:目标文件的指针
  • 返回值:成功返回写入的字符,位置指针向后移动一个字节;失败返回 EOF
  • 使用方法:fputc('a', 文件指针);

3)fgets:从文件读取一个字符串到字符数组中

  • 函数原型:char* fgets(char *Buffer, int MaxCount, FILE *file );
    • Buffer:字符数组的指针
    • MaxCount:最大读取字符数
    • file:目标文件的指针
  • 说明:
    • MaxCount是一个正整数,表示从文件中读出的字符串不超过 MaxCount-1个字符。在读入的最后一个字符后加上串结束标志 \0
    • 在读出MaxCount-1个字符之前,如遇到了换行符或EOF,则读出结束。
  • 返回值:字符数组的首地址
  • 使用方法:fgets(数组首地址, 字符串最大长度, 文件指针);

4)fputs:将一个字符串写入到文件中,不包含’\0’

  • 函数原型:int fputs(const char *str, FILE *file);
    • str:要写入的字符数组(字符串)的指针
    • file:目标文件的指针
  • 返回值:成功返回非负整数;失败返回 EOF(符号常量,其值为-1)
  • 使用方法:fputs(字符串, 文件指针);

5)fread:从文件中读取一组固定大小的数据到内存空间

  • 函数原型:size_t fread(void *Buffer, size_t size, size_t count, FILE *file);
    • Buffer:内存空间首地址(用来存放数据的内存空间指针)
    • size:数据块的大小
    • count:数据块的数量
    • file:目标文件的指针
  • 返回值:返回成功读取的对象个数(若出现错误或到达文件末尾,则可能小于count)
  • 使用方法:fread(内存空间地址, 数据块大小, 数据块数量, 文件指针);

6)fwrite:写入一组固定大小的数据到文件中

  • 函数原型:size_t fwrite(const void *Buffer, size_t size, size_t count, FILE *file);
    • Buffer:要存入的数据的首地址
    • size:数据块的大小
    • count:数据块的数量
    • file:目标文件的指针
  • 返回值:返回成功写入的对象个数(若出现错误或到达文件末尾,则可能小于 count)
  • 使用方法:fwrite(数据地址, 数据块大小, 数据块数量, 文件指针);

7)fscanf:从文件中获取指定格式的数据,跟scanf类似,输入对象换成了普通文件

  • 函数原型:int fscanf(FILE *file, const char *str, [arg...]);
    • file:目标文件的指针
    • str:格式化字符串
    • [arg…]:一个或多个接收数据的地址
  • 说明:fscanf 遇到空格和换行时结束
  • 返回值:成功返回读入参数的个数,失败返回 EOF
  • 使用方法:fscanf(文件指针, 格式化字符串, 目标地址);

8)fprintf:格式化输出数据到文件,跟printf类似,输出对象换成了普通文件

  • 函数原型:int fprint(FILE *file, const char *str, [arg...]);
    • file:目标文件的指针
    • str:格式化字符串
    • [arg…]:一个或多个数据
  • 说明:fprintf 会根据参数 str 字符串来转换并格式化数据,然后将结果输出到参数file指定的文件中,直到出现字符串结束(\0)为止。
  • 返回值:成功返回输出数据的个数,失败返回 EOF
  • 使用方法:fprintf(文件指针, 格式化字符串, 目标数据);

(3)文件操作实例

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

int main(){
    FILE *fp = fopen("test.txt", "w+"); // 以读写模式打开一个文件
    if (fp == NULL) {
        printf("文件打开失败!");
        exit(1);
    }
    fputc('a', fp); // 向文件写入一个字符'a'
    rewind(fp); // 将文件位置指针放到文件头部,因为我们刚刚向文件写入了一个字符'a',所以现在文件位置指针指向的文件尾部

    char ch = (char)fgetc(fp); // 从文件读取一个字符,现在文件中只有一个'a',读取的字符就是'a'
    printf("%c\n",ch);
    printf("结束位置:%d\n", feof(fp)); // 看看位置指针是不是在结束位置

    fclose(fp); //关闭文件

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1627304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

陕西省人力资源和社会保障厅 陕西省住房和城乡建设厅 关于开展2023年度全省建设工程专业高级工程师评审工作的通知

陕西工程系列建设工程专业工程师评审工作要求链接陕西省人力资源和社会保障厅 陕西省住房和城乡建设厅 关于开展2023年度全省建设工程专业高级工程师评审工作的通知 - 陕西省住房和城乡建设厅类别基本条件业绩成果备注助理工程师 最新公告http://www.snhrm.com/zxggao2/597358…

怎么排查K8S容器当中的Java程序内存泄露问题

今天早上发现生产线其中的一个服务在凌晨的时候突然重启了&#xff0c;内存突然从1G升到1.8G&#xff0c;CPU使用量从0.1升到了0.28&#xff0c;说明在这个时间点&#xff0c;内存突增达到了限额以上&#xff0c;服务重启了。因为这个服务布署了多节点&#xff0c;这次重启对业…

不同技术实现鼠标滚动图片的放大缩小

摘要&#xff1a; 最近弄PC端的需求时&#xff0c;要求在layui技术下实现鼠标滚动图片的放大缩小的功能&#xff01;下面来总结一下不同框架剩下这功能&#xff01; layui: 看了一下layui文档&#xff0c;其实这有自带的组件的&#xff01;但是又版本要求的!并且layui的官方文档…

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…

北京车展创新纷呈,移远通信网联赋能

时隔四年&#xff0c;备受瞩目的2024&#xff08;第十八届&#xff09;北京国际汽车展览会于4月25日盛大开幕。在这场汽车行业盛会上&#xff0c;各大主流车企竞相炫技&#xff0c;众多全球首发车、概念车、新能源车在这里汇聚&#xff0c;深刻揭示了汽车产业的最新成果和发展潮…

某赛通电子文档安全管理系统 多处 SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

函数模板与类模板初阶

如果要写一个交换函数&#xff0c;不同类型的话调用不同的交换函数&#xff0c;如果使用重载的话只能解决函数名相同但是会根据参数类型调用不同的函数。即使这样也依旧要写很多不同类型的swap交换函数 函数重载的交换函数 仔细观察会发现除了类型不同其他的函数结构什么的都一…

Postman 在 Linux 上的安装指南:简单快速开始使用

本文将介绍如何在 Linux 上安装 Postman 的详细步骤&#xff0c;Postman 支持的 Linux 的发行版包括&#xff1a;Ubuntu 14.04 及更高版本&#xff0c;Fedora 24&#xff0c;Debian 8 及更高版本。下面将介绍其具体的安装方法。 手动安装 Postman 的下载地址&#xff0c;下载…

一、Django 初识

简介 Django 是一个用于构建 Web 应用程序的高级 Python Web 框架。 版本对应 不同版本的django框架是基于特定的不同的python版本开发的&#xff0c;所以不同版本的django框架要正常执行功能只能安装特定的python版本 Django安装 安装 Django # 全局安装 pip install dj…

泰坦尼克号乘客生存情况预测分析2

泰坦尼克号乘客生存情况预测分析1 泰坦尼克号乘客生存情况预测分析2 泰坦尼克号乘客生存情况预测分析3 泰坦尼克号乘客生存情况预测分析总 背景描述 Titanic数据集在数据分析领域是十分经典的数据集&#xff0c;非常适合刚入门的小伙伴进行学习&#xff01; 泰坦尼克号轮船的…

ionic 中对Input输入框、searchbar进行solr检索

一、概述 Ionic 是一个用于开发跨平台应用程序的开源工具&#xff0c;可以使用 Angular、React 或 Vue 等前端框架。要在 Ionic 应用程序中实现实时与 Solr 通信&#xff0c;可以使用 HTTP 客户端&#xff08;如 Angular 的 HttpClient 或 Ionic 的 Native HTTP&#xff09;…

笔记:编写程序,绘制一个展示 2013~2019 财年阿里巴 巴淘宝+天猫平台的 GMV 的柱形图,实现过程如下:

文章目录 前言一、GMV 的柱形图是什么&#xff1f;二、编写代码总结 前言 编写程序。根据实例 2 的要求&#xff0c;绘制一个展示 2013~2019 财年阿里巴 巴淘宝天猫平台的 GMV 的柱形图&#xff0c;实现过程如下&#xff1a; &#xff08;1&#xff09; 导入 matplotlib.pypl…

Linux快速部署大语言模型LLaMa3,Web可视化j交互(Ollama+Open Web UI)

本文在个人博客同步发布&#xff0c;前往阅读 1 介绍 本文将介绍使用开源工具Ollama(60.6k⭐)部署LLaMa大模型&#xff0c;以及使用Open WebUI搭建前端Web交互界面的方法。 我们先来过一遍几个相关的概念&#xff0c;对这块比较熟悉的朋友可跳过。 1.1 大规模语言模型 大规…

从递归角度串联二叉树-图论-动态规划

一、深度理解二叉树的前中后序遍历 二叉树遍历框架如下&#xff1a; void traverse(TreeNode* root) {if (root nullptr) {return;}// 前序位置traverse(root->left);// 中序位置traverse(root->right);// 后序位置 }先不管所谓前中后序&#xff0c;单看 traverse 函数…

keytool,openssl的使用

写在前面 在生成公钥私钥&#xff0c;配置https时经常需要用到keytool&#xff0c;openssl工具&#xff0c;本文就一起看下其是如何使用的。 keytool是jdk自带的工具&#xff0c;不需要额外下载&#xff0c;但openssl需要额外下载 。 1&#xff1a;使用keytool生成jks私钥文件…

Office Word自动编号转文本

原理 使用office自带的宏功能&#xff0c;一键替换 过程 调出word的“开发工具”选项 文件->选项->自定义功能区->选中开发工具->确定 创建宏 开发工具->宏->创建宏 编写宏 在弹出来的框里&#xff0c;替换代码为 Sub num2txt() ActiveDocument.…

ArcGIS批量寻找图层要素中的空洞

空洞指的是图层中被要素包围所形成的没有被要素覆盖的地方&#xff0c;当图层要素数量非常庞大时&#xff0c;寻找这些空洞就不能一个一个的通过目测去寻找了&#xff0c;需要通过使用工具来实现这一目标。 一、【要素转线】工具 利用【要素转线】工具可以将空洞同图层要素处于…

电商技术揭秘三十五:智能风控功能架构浅析

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…

WEB攻防-PHP特性-CMS审计实例

前置知识&#xff1a;PHP函数缺陷 测试环境&#xff1a;MetInfo CMS 函数缺陷导致的任意文件读取 漏洞URL&#xff1a;/include/thumb.php?dir 漏洞文件位置&#xff1a;MetInfo6.0.0\app\system\include\module\old_thumb.class.php <?phpdefined(IN_MET) or exit(No…

ElasticSearch语句中must,must_not,should 组合关系

前言&#xff1a; 在实际应用中&#xff0c;发现当bool中同时使用must和should 没有达到想要的想过&#xff0c;而是只展示了must中的命中数据&#xff0c;所以打算探究一下bool中 三种逻辑关系的组合。 上述查询语句只展示了must的结果&#xff0c;没有should中的结果&#…