C语言学习

本文详细介绍了C语言的基本概念,包括数据类型、判断、循环、函数、作用域规则、数组、指针、字符串、结构体、共用体、文件读写和动态数组。还探讨了指针的使用、函数指针、动态链表的创建和操作。通过实例展示了如何创建、操作和管理C语言中的各种数据结构和控制流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录


C语言

C 语言是一种通用的、面向过程式的计算机程序设计语言。
其基本形式如下:

#include <stdio.h>
 
int main()
{
    /* 我的第一个 C 程序 */
    printf("Hello, World! \n");
 
    return 0;
}

一、数据类型

在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。
C 中的类型可分为以下几种:
在这里插入图片描述

1、整型数据

在这里插入图片描述
为了得到某个类型或某个变量在特定平台上的准确大小,您可以使用 sizeof 运算符。表达式 sizeof(type) 得到对象或类型的存储字节大小。

2、浮点类型

在这里插入图片描述

3、void类型

void 类型指定没有可用的值。它通常用于以下三种情况下:
在这里插入图片描述

二、判断

1、判断语句

在这里插入图片描述

2、? : 运算符(三元运算符)

条件运算符 ? :,可以用来替代 if…else 语句。它的一般形式如下:

Exp1 ? Exp2 : Exp3;

其中,Exp1、Exp2 和 Exp3 是表达式。请注意,冒号的使用和位置。

? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个表达式的值。
例如:
以下实例通过输入一个数字来判断它是否为奇数或偶数:

#include<stdio.h>
 
int main()
{
    int num;
 
    printf("输入一个数字 : ");
    scanf("%d",&num);
 
    (num%2==0)?printf("偶数"):printf("奇数");
}

三、循环

1、循环类型

在这里插入图片描述

2、循环控制语句

循环控制语句改变你代码的执行顺序。通过它你可以实现代码的跳转。
在这里插入图片描述

3、无限循环

如果条件永远不为假,则循环将变成无限循环。for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。

#include <stdio.h>
 
int main ()
{
   for( ; ; )
   {
      printf("该循环会永远执行下去!\n");
   }
   return 0;
}

注意:您可以按 Ctrl + C 键终止一个无限循环。

四、函数

函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。
我们可以定义一个新的函数,然后在主函数中运用它,这样可以简便主函数的程序。

1、定义函数

C 语言中的函数定义的一般形式如下:

return_type function_name( parameter list )
{
   body of the function
}

在 C 语言中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:
①返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型(还可以是int、float、double等)。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
②函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
③参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
④函数主体:函数主体包含一组定义函数执行任务的语句。
实例:
以下是 max() 函数的源代码。该函数有两个参数 num1 和 num2,会返回这两个数中较大的那个数:

/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

2、函数声明

函数声明包括以下几个部分:

return_type function_name( parameter list );

针对上面定义的函数 max(),以下是函数声明:

int max(int num1, int num2);

在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此可以不写参数的名称。

3、调用函数

当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。
调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。例如:

#include <stdio.h>
 
/* 函数声明 */
int max(int num1, int num2);
 
int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
   int ret;
 
   /* 调用函数来获取最大值 */
   ret = max(a, b);
 
   printf( "Max value is : %d\n", ret );
 
   return 0;
}
 
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

4、函数参数

如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。

形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。

当调用函数时,有两种向函数传递参数的方式:
在这里插入图片描述
默认情况下,C 使用传值调用来传递参数。一般来说,这意味着函数内的代码不能改变用于调用函数的实际参数。

五、C 作用域规则

任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。C 语言中有三个地方可以声明变量:

在函数或块内部的局部变量
在所有函数外部的全局变量
在形式参数的函数参数定义中

1、局部变量

在某个函数或块的内部声明的变量称为局部变量。它们只能被该函数或该代码块内部的语句使用。局部变量在函数外部是不可知的。下面是使用局部变量的实例。在这里,所有的变量 a、b 和 c 是 main() 函数的局部变量。

#include <stdio.h>
 
int main ()
{
  /* 局部变量声明 */
  int a, b;
  int c;
 
  /* 实际初始化 */
  a = 10;
  b = 20;
  c = a + b;
 
  printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
 
  return 0;
}

2、全局变量

全局变量是定义在函数外部,通常是在程序的顶部。全局变量在整个程序生命周期内都是有效的,在任意的函数内部能访问全局变量。

全局变量可以被任何函数访问。也就是说,全局变量在声明后整个程序中都是可用的。下面是使用全局变量和局部变量的实例:

在这里插入代码片
```#include <stdio.h>
 
/* 全局变量声明 */
int g;
 
int main ()
{
  /* 局部变量声明 */
  int a, b;
 
  /* 实际初始化 */
  a = 10;
  b = 20;
  g = a + b;
 
  printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
 
  return 0;
}
在程序中,局部变量和全局变量的名称可以相同,但是在函数内,如果两个名字相同,会使用局部变量值,全局变量不会被使用。下面是一个实例:

```c
#include <stdio.h>
 
/* 全局变量声明 */
int g = 20;
 
int main ()
{
  /* 局部变量声明 */
  int g = 10;
 
  printf ("value of g = %d\n",  g);
 
  return 0;
}

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

value of g = 10

3、形式参数

函数的参数,形式参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用。

4、初始化局部变量和全局变量

当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化,如下所示:
在这里插入图片描述

六、数组

C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。

数组的声明并不是声明一个个单独的变量,比如 runoob0、runoob1、…、runoob99,而是声明一个数组变量,比如 runoob,然后使用 runoob[0]、runoob[1]、…、runoob[99] 来代表一个个单独的变量。

所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
在这里插入图片描述

1、声明数组

在 C 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:

type arrayName [ arraySize ];

这叫做一维数组。arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组 balance,声明语句如下:

double balance[10];

现在 balance 是一个可用的数组,可以容纳 10 个类型为 double 的数字。

二维数组的声明形式

type arrayName [ arraySize ][ arraySize ];

各字符意味与上面所述相同。

2、初始化数组

①我们可以使用一个初始化语句

如下所示:

double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};

大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
我们也可以不在方括号[ ]中声明元素个数,例如:

double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

②我们也可以逐个初始化数组

我们可以先创建一个数组,然后再一个一个赋值,例如:

int a[5];
a[0]=0;
a[1]=1;
a[2]=2;
a[3]=3;
a[4]=4;

3、访问数组元素

数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如:

double salary = balance[9];//将把数组中第 10 个元素的值赋给 salary 变量

索引也就是方括号内的数。

4、多维数组

多维数组中最常见的就是二维数组,其声明、初始化、访问与一维数组类似,只是n维数组后要加n个方括号,例如

int a[3][2][1];//声明一个三维数组

七、指针

指针也就是内存地址,指针变量是用来存放内存地址的变量。:

1、指针的使用

使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include <stdio.h>
 
int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */
 
   ip = &var;  /* 在指针变量中存储 var 的地址 */
 
   printf("var 变量的地址: %p\n", &var  );
 
   /* 在指针变量中存储的地址 */
   printf("ip 变量存储的地址: %p\n", ip );
 
   /* 使用指针访问值 */
   printf("*ip 变量的值: %d\n", *ip );
 
   return 0;
}

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

var 变量的地址: 0x7ffeeef168d8
ip 变量存储的地址: 0x7ffeeef168d8
*ip 变量的值: 20

用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。int *与int一样是数据类型,不能认为数据类型为int,而变量名称为 *ip;
注意:

int *p;
int a=10;
p=&a;
//p是a的地址,*p是a的值;

2、C 中的 NULL 指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

#include <stdio.h>
 
int main ()
{
   int  *ptr = NULL;
 
   printf("ptr 的地址是 %p\n", ptr  );
 
   return 0;
}

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

ptr 的地址是 0x0

3、C 指针的算术运算

①加减指针的运算

C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-。
假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
总结:
①指针的每一次递增,它其实会指向下一个元素的存储单元。
②指针的每一次递减,它都会指向前一个元素的存储单元。
③指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。

②指针的比较

指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

下面的程序只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   /* 指针中第一个元素的地址 */
   ptr = var;
   i = 0;
   while ( ptr <= &var[MAX - 1] )
   {
 
      printf("存储地址:var[%d] = %p\n", i, ptr );
      printf("存储值:var[%d] = %d\n", i, *ptr );
 
      /* 指向上一个位置 */
      ptr++;
      i++;
   }
   return 0;
}

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

存储地址:var[0] = 0x7ffeee2368cc
存储值:var[0] = 10
存储地址:var[1] = 0x7ffeee2368d0
存储值:var[1] = 100
存储地址:var[2] = 0x7ffeee2368d4
存储值:var[2] = 200

4、指针数组

①让数组存储指向 int 或 char 或其他数据类型的指针

下面是一个指向整数的指针数组的声明:

int *ptr[MAX];

在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。
下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int i, *ptr[MAX];
 
   for ( i = 0; i < MAX; i++)
   {
      ptr[i] = &var[i]; /* 赋值为整数的地址 */
   }
   for ( i = 0; i < MAX; i++)
   {
      printf("Value of var[%d] = %d\n", i, *ptr[i] );
   }
   return 0;
}

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

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

②用一个指向字符的指针数组来存储一个字符串列表

实例:

#include <stdio.h>
 
const int MAX = 4;
 
int main ()
{
   const char *names[] = {
                   "Zara Ali",
                   "Hina Ali",
                   "Nuha Ali",
                   "Sara Ali",
   };
   int i = 0;
 
   for ( i = 0; i < MAX; i++)
   {
      printf("Value of names[%d] = %s\n", i, names[i] );
   }
   return 0;
}

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

Value of names[0] = Zara Ali
Value of names[1] = Hina Ali
Value of names[2] = Nuha Ali
Value of names[3] = Sara Ali

5、指向指针的指针

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:

int **var;

实例:

#include <stdio.h>
 
int main ()
{
   int  V;
   int  *Pt1;
   int  **Pt2;
 
   V = 100;
 
   /* 获取 V 的地址 */
   Pt1 = &V;
 
   /* 使用运算符 & 获取 Pt1 的地址 */
   Pt2 = &Pt1;
 
   /* 使用 pptr 获取值 */
   printf("var = %d\n", V );
   printf("Pt1 = %p\n", Pt1 );
   printf("*Pt1 = %d\n", *Pt1 );
    printf("Pt2 = %p\n", Pt2 );
   printf("**Pt2 = %d\n", **Pt2);
 
   return 0;
}

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

var = 100
Pt1 = 0x7ffee2d5e8d8
*Pt1 = 100
Pt2 = 0x7ffee2d5e8d0
**Pt2 = 100

6、传递指针给函数

C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

#include <stdio.h>
#include <time.h>
 
void getSeconds(unsigned long *par);

int main ()
{
   unsigned long sec;


   getSeconds( &sec );

   /* 输出实际值 */
   printf("Number of seconds: %ld\n", sec );

   return 0;
}

void getSeconds(unsigned long *par)
{
   /* 获取当前的秒数 */
   *par = time( NULL );
   return;
}

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

Number of seconds :1294450468

能接受指针作为参数的函数,也能接受数组作为参数,如下所示:

#include <stdio.h>
 
/* 函数声明 */
double getAverage(int *arr, int size);
 
int main ()
{
   /* 带有 5 个元素的整型数组  */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
 
   /* 传递一个指向数组的指针作为参数 */
   avg = getAverage( balance, 5 ) ;
 
   /* 输出返回值  */
   printf("Average value is: %f\n", avg );
   
   return 0;
}

double getAverage(int *arr, int size)
{
  int    i, sum = 0;      
  double avg;          
 
  for (i = 0; i < size; ++i)
  {
    sum += arr[i];
  }
 
  avg = (double)sum / size;
 
  return avg;
}

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

Average value is: 214.40000

7、从函数返回指针

为了做到这点,我们必须声明一个返回指针的函数,如下所示:

int * myFunction()
{
.
.
.
}

另外,C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。

现在,让我们来看下面的函数,它会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下:

#include <stdio.h>
#include <time.h>
#include <stdlib.h> 
 
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
   static int  r[10];
   int i;
 
   /* 设置种子 */
   srand( (unsigned)time( NULL ) );
   for ( i = 0; i < 10; ++i)
   {
      r[i] = rand();
      printf("%d\n", r[i] );
   }
 
   return r;
}
 
/* 要调用上面定义函数的主函数 */
int main ()
{
   /* 一个指向整数的指针 */
   int *p;
   int i;
 
   p = getRandom();
   for ( i = 0; i < 10; i++ )
   {
       printf("*(p + [%d]) : %d\n", i, *(p + i) );
   }
 
   return 0;
}

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

1523198053
1187214107
1108300978
430494959
1421301276
930971084
123250484
106932140
1604461820
149169022
*(p + [0]) : 1523198053
*(p + [1]) : 1187214107
*(p + [2]) : 1108300978
*(p + [3]) : 430494959
*(p + [4]) : 1421301276
*(p + [5]) : 930971084
*(p + [6]) : 123250484
*(p + [7]) : 106932140
*(p + [8]) : 1604461820
*(p + [9]) : 149169022

8、函数指针

函数指针可以像一般函数一样,用于调用函数、传递参数。

函数指针变量的声明:

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

实例
以下实例声明了函数指针变量 p,指向函数 max:

#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    //声明一个指向与函数max同样参数、返回值的函数指针p
    int a, b, c, d;
 
    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);
 
    /* 与直接调用函数等价,d = max(max(a, b), c) */
    d = p(p(a, b), c); 
 
    printf("最大的数字是: %d\n", d);
 
    return 0;
}

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

请输入三个数字:1 2 3
最大的数字是: 3

八、字符串

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

下面的声明和初始化创建了一个 RUNOOB 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 RUNOOB 的字符数多一个。

char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};

依据数组初始化规则,您可以把上面的语句写成以下语句:

char site[] = "RUNOOB";

其实,我们不需要把 null 字符放在字符串常量的末尾。C 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。
C 中有大量操作字符串的函数:

在这里插入图片描述
而在运用这些函数之前,我们需要先标注一个头文件,例如:

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

九、结构体

结构是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。就像一个学生的期末成绩,包括语文、数学、英语等多个数据。

1、定义结构

为了定义结构,我们必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:

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

tag 是结构体标签。

member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。

variable-list 结构变量,定义在结构的末尾,最后一个分号之前,我们可以指定一个或多个结构变量,我们也可以先定义后声明。

2、结构体变量的初始化

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

3、访问结构成员

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

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
}
int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   Book1.title="C Programming";
 

4、结构作为函数参数

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

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
void printBook( struct Books book )
{
   printf( "Book title : %s\n", book.title);
   printf( "Book author : %s\n", book.author);
   printf( "Book subject : %s\n", book.subject);
   printf( "Book book_id : %d\n", book.book_id);
}

5、指向结构的指针

实例:

#include <stdio.h>
#include <string.h>
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
 
/* 函数声明 */
void printBook( struct Books *book );
int main( )
{
   struct Books Book1;        /* 声明 Book1,类型为 Books */
   struct Books Book2;        /* 声明 Book2,类型为 Books */
 
   /* Book1 详述 */
   strcpy( Book1.title, "C Programming");
   strcpy( Book1.author, "Nuha Ali"); 
   strcpy( Book1.subject, "C Programming Tutorial");
   Book1.book_id = 6495407;
 
   /* Book2 详述 */
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Zara Ali");
   strcpy( Book2.subject, "Telecom Billing Tutorial");
   Book2.book_id = 6495700;
 
   /* 通过传 Book1 的地址来输出 Book1 信息 */
   printBook( &Book1 );
 
   /* 通过传 Book2 的地址来输出 Book2 信息 */
   printBook( &Book2 );
 
   return 0;
}
void printBook( struct Books *book )
{
   printf( "Book title : %s\n", book->title);
   printf( "Book author : %s\n", book->author);
   printf( "Book subject : %s\n", book->subject);
   printf( "Book book_id : %d\n", book->book_id);
}

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

Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700

为了使用指向该结构的指针访问结构的成员,我们必须使用 -> 运算符

十、共用体

共用体的用法与结构体类似

十一、文件读写

1、打开文件

我们可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:

FILE *fopen( const char * filename, const char * mode );

在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
在这里插入图片描述
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:

"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"

2、关闭文件

为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:

int fclose( FILE *fp );

如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。

3、写入文件

①函数 fputc

int fputc( int c, FILE *fp );

函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。

②函数 fputs

int fputs( const char *s, FILE *fp );

函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。

③函数fprintf

 int fprintf(FILE *fp,const char *format, ...)

函数 fprintf()把一个字符串写入到文件中
实例:

#include <stdio.h>
 
int main()
{
   FILE *fp = NULL;
 
   fp = fopen("/tmp/test.txt", "w+");
   fprintf(fp, "This is testing for fprintf...\n");
   fputs("This is testing for fputs...\n", fp);
   fclose(fp);
}

当上面的代码被编译和执行时,它会在 /tmp 目录中创建一个新的文件 test.txt,并使用两个不同的函数写入两行。接下来让我们来读取这个文件。

4、读取文件

①fgetc函数

int fgetc( FILE * fp );

fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。

② fgets函数

char *fgets( char *buf, int n, FILE *fp );

函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。

③fscanf函数

 int fscanf(FILE *fp, const char *format, ...) 

fscanf函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。
实例:

#include <stdio.h>
 
int main()
{
   FILE *fp = NULL;
   char buff[255];
 
   fp = fopen("/tmp/test.txt", "r");
   fscanf(fp, "%s", buff);
   printf("1: %s\n", buff );
 
   fgets(buff, 255, (FILE*)fp);
   printf("2: %s\n", buff );
   
   fgets(buff, 255, (FILE*)fp);
   printf("3: %s\n", buff );
   fclose(fp);
 
}

当上面的代码被编译和执行时,它会读取上一部分创建的文件,产生下列结果:

1: This
2: is testing for fprintf...

3: This is testing for fputs...

首先,fscanf() 方法只读取了 This,因为它在后边遇到了一个空格。其次,调用 fgets() 读取剩余的部分,直到行尾。最后,调用 fgets() 完整地读取第二行。

5、二进制 I/O 函数

下面两个函数用于二进制输入和输出:

size_t fread(void *ptr, size_t size_of_elements, 
             size_t number_of_elements, FILE *a_file);
              
size_t fwrite(const void *ptr, size_t size_of_elements, 
             size_t number_of_elements, FILE *a_file);

这两个函数都是用于存储块的读写 - 通常是数组或结构体。

十二、动态数组

1、常用的内存动态分配函数

以前开辟内存是静态开辟,比如:

int a;

静态开辟内存是在栈中开辟固定大小的内存。
动态开辟内存是向堆里申请内存空间,可以通过malloc()函数,calloc()函数来实现。在堆中申请的内存空间不会自动释放内存,需要我们手动释放,就需要free()函数来完成。

①malloc函数

malloc函数会向堆中申请一片连续的可用内存空间,若申请成功,返回指向这片内存空间的指针,若失败,则会返回NULL,所以我们在用malloc函数开辟动态内存后,一定要判断函数返回值是否为NULL。
在这里插入图片描述

②calloc函数

在这里插入图片描述

2、动态数组

举例:
在这里插入图片描述

十三、链表

1、基本概念

在这里插入图片描述
在这里插入图片描述

2、创建动态链表

其实我们可以通过创建一个函数,最后通过主函数来输出。

#include<stdio.h>
#include<stdlib.h>
struct node
{
	char info[20];
	int no;
	int grade;
	struct node *next;
};
struct node *h,*p,*q,*s;
int i,n;
void creat ()
{
	h=NULL;p=h;q=h;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		p=(struct node *)malloc(sizeof(struct node));
		scanf("%s%d%d",p->info,&p->no,&p->grade);
		p->next=NULL;
		if(i==1)
		{h=p;q=p;}
		else
		{q->next=p;q=p;}
	}
}

3、输出动态链表

同上也是通过函数来实现。

void print()
{
	p=h;
	while(p)
	{
		printf("%s:%d->",p->info,p->no);
		p=p->next;
	}
}

4、动态链表的删除操作

在这里插入图片描述

5、动态链表的插入

在这里插入图片描述

6创建、输出、插入的实例

编写程序。创建链表并完成链表的插入操作。
结点类型如下:

  struct node 
  {  
  char info[20]; 
   int no;  
   int grade;  
   struct node *next;
   };

要求: (1)采用后插建立链表法创建一个包含n个结点的链表,结点个数n及各结点成员的值均由键盘输入。说明:输入数据时,各结点成员值按grade值从小到大的顺序初始化。 (2)输入待插入结点成员的值,并将该结点有序插入到链表中,最后输出各结点info和no成员的值,输出格式见测试数据。 (3)请使用动态链表实现,否则不得分。 (提示:考试系统中使用scanf函数,但不能使用scanf_s;主函数使用int main(){return 0;}形式,但不能使用void main(){}。) 输入输出格式请严格按如下示例要求,格式不正确不得分。
示例1: 输入数据为: 1 a 2 2 b 1 1 输出结果为: b:1->a:2->
输入数据格式说明: 1 表示n的值 a 2 2 表示n个结点的值,各结点按info,no,grade顺序输入 b 1 1 表示待插入结点的值,按info,no,grade顺序输入

#include<stdio.h>
#include<stdlib.h>
struct node
{
	char info[20];
	int no;
	int grade;
	struct node *next;
};
struct node *h,*p,*q,*s;
int i,n;
void creat ()
{
	h=NULL;p=h;q=h;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		p=(struct node *)malloc(sizeof(struct node));
		scanf("%s%d%d",p->info,&p->no,&p->grade);
		p->next=NULL;
		if(i==1)
		{h=p;q=p;}
		else
		{q->next=p;q=p;}
	}
}
void print()
{
	p=h;
	while(p)
	{
		printf("%s:%d->",p->info,p->no);
		p=p->next;
	}
}
void in()
{
	s=(struct node *)malloc(sizeof(struct node));
	scanf("%s%d%d",s->info,&s->no,&s->grade);
	if(h==NULL)
	{h=s;s->next=NULL;}
	else
	{
		if(h->grade>s->grade)
		{s->next=h;h=s;}
		else
		{
			p=h;
			while(p->next&&p->next->grade<s->grade)
			{p=p->next;}
			s->next=p->next;
			p->next=s;
		}
	}
}
int main()
{
	creat();
	in();
	print();
	printf("\n");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值