软件面试笔试复习之C语言

本篇为软件开发工程师打造,从入门到复习,巩固知识点,从而提高自己笔试或面试的知识水平。C/C++编程技能是嵌入式软件开发最常用的编程语言,因此熟练掌握是非常有必要的。

开篇

C语言的学习永远绕不开的程序“Hello world!”

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

所有C语言程序都需要包含main()函数,代码也是从main函数开始执行。

然后printf()在头文件stdio.h中声明,#include是一个预处理命令用于引入头文件。

return 0语句表示程序结束退出。

C程序结构主要包括有:预处理指令、函数、变量、语句&表达式、注释

根据“Hello world!”程序

#include <stdio.h>就是预处理指令;int main和printf都是函数,但int main是主函数,程序从这里开始执行;/* 我的第一个 C 程序 */就是注释,以“//”开始的是单行注释。

基本语法

C程序由关键字、标识符、常量、字符串值,或者是一个符号等组成,在C程序中,分号“;”是语句结束符,语句必须以分号结束。

标识符用户用于标识变量、函数的名称,一个标识符以字母 A-Z 或 a-z 或下划线“_”开始。注意:C 标识符内不允许出现标点字符,比如@、$和%,C是区分大小写的编程语言。

关键字就是C语言中保留字,不能作为标识符名称。例如有:auto、double、int、struct、break、else、long、switch、case、enum、register、typedef、char、extern、return、union、const、float、short、unsigned、continue、for、signed、void、default、goto、sizeof、volatile、do、if、while、static。。。

C中的空格编译器会直接忽略的,但是为了美观增加可读性,可以根据需要适当添加一些空格。

数据类型

基本数据类型(算术类型):整型(int)、字符型(char)、浮点型(float)和双精度浮点型(double)。

枚举类型:也是算术类型,常用来定义在程序中只能赋予其一定的离散整数值的变量。

void类型:没有值的数据类型,通常用于函数返回值。

派生类型:数组、指针和结构体类型。

具体地,

整数类型存储大小:

char 1字节、unsigned char 1字节、signed char 1字节、int 2或4字节、unsighted int 2或4字节、short 2字节、unsigned short 2字节、long 4字节、 unsigned long 4字节。

2还是4取决于系统位数有关,就是32位系统就是2字节,64位系统就是4字节。

浮点类型存储大小与精度范围:

float 4字节,6为有效位;double 8字节,15位有效位; long double 16字节,19位有效位。

存储大小笔试选择题经常考到。

void类型:

函数返回空,例如void xxjxx(int a);函数参数为空,例如int xxjxx(void);

指针指向void,类型为void* 的指针代表对象的地址,而不是类型,例如内存分配函数

void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。

类型转换:

C 语言中有隐式类型转换和显式类型转换。

隐式类型转换:隐式类型转换是在表达式中自动发生的,无需进行任何明确的指令或函数调用。可能会导致数据精度丢失或数据截断。

例如:

int a=10;

double b = 1.0 + a; //隐式将int类型转换为double类型

显式类型转换:显式类型转换需要使用强制类型转换运算符。

double d = 3.14159;

int c = (int)d; // 显式将double类型转换为int类型

变量

变量定义:

int    i, j, k;

char   c, ch;

float  f, salary;

double d;

变量可以在声明的时候被初始化。

extern int d = 3, f = 5;    // d 和 f 的声明与初始化

int d = 3, f = 5;           // 定义并初始化 d 和 f

byte z = 22;                // 定义并初始化 z

char x = 'x';               // 变量 x 的值为 'x'

变量的声明有两种情况:

1、需要建立存储空间的。例如:int i 在声明的时候就已经建立了存储空间。

2、不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。例如:extern int d 其中变量d 可以在别的文件中定义的。

例子:

#include <stdio.h>
 
// 函数外定义变量 x 和 y
int x;
int y;
int addtwonum()
{
    // 函数内声明变量 x 和 y 为外部变量
    extern int x;
    extern int y;
    // 给外部变量(全局变量)x 和 y 赋值
    x = 1;
    y = 2;
    return x+y;
}
 
int main()
{
    int result;
    // 调用函数 addtwonum
    result = addtwonum();
    
    printf("result 为: %d",result);
    return 0;
}

输出结果:result 为:3

如果需要在一个源文件引用另一个源文件中定义的变量,只需要在引用的文件中将变量加上 extern 关键字的声明即可。

C中的左值和右值

1、左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。

2、右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的具体数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。

int g = 20;             //有效语句。数值型的字面值是右值,不能出现在赋值号的左边。

10 = 20;                //编译报错

常量

整数常量:是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

实例:

212        /* 合法的 */

215u       /* 合法的 */

0xFeeL     /* 合法的 */

078        /* 非法的:8 不是八进制的数字 */

032UU      /* 非法的:不能重复后缀 */

常用:

85        /* 十进制 */

0213      /* 八进制 */

0x4b      /* 十六进制 */

30        /* 整数 */

30u       /* 无符号整数 */

30l       /* 长整数 */

30ul      /* 无符号长整数 */

例如:

int myInt = 10;

long myLong = 100000L;

unsigned int myUnsignedInt = 10U;

浮点常量:由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。带符号的指数是用 e 或 E 引入的。

3.14159       /* 合法的 */

314159E-5L    /* 合法的 */

510E          /* 非法的:不完整的指数 */

210f          /* 非法的:没有小数或指数 */

.e55          /* 非法的:缺少整数或分数 */

例如:

float myFloat = 3.14f;

double myDouble = 3.14159;

字符常量:括在单引号中,例如,'x'。可以是一个普通的字符(例如'x')、一个转义序列(例如'\t'),或一个通用的字符(例如'\u02C0')。

转义序列码:

转义序列

含义

转义序列

含义

\\

\字符

\n

换行符

\'

'字符

\r

回车

\''

''字符

\t

水平制表符

\?

?字符

\v

垂直制表符

\a

报警铃声

\ooo

一到三位的八进制数

\b

退格键

\xhh

一到三位16进制数

\f

换页符

字符常量的 ASCII 值可以通过强制类型转换转换为整数值。

例如:

char myChar = 'a';

int myAsciiValue = (int) myChar; // 将 myChar 转换为 ASCII 值 97

字符串常量:字面值或常量是括在双引号 " " 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。

例如:

char myString[] = "Hello, world!"; //系统对字符串常量自动加一个'\0'

定义常量

1、使用#define预处理器

2、使用const关键字

面试经常会问这两种定义常量的方式的异同。

#define 预处理器:

注意:#define LENGTH1 10和#define LENGTH2 5+5的区别

例如:

#include <stdio.h>
 
#define LENGTH1 10   
#define LENGTH2 5+5

int main()
{
   int area1, area2;  
   area1 = LENGTH1 * 2;
   area2 = LENGTH2 * 2;
   printf("value of area1 : %d", area1);		// 10*2 = 20
   printf("value of area2 : %d", area2);		// 5+5*2=15

   return 0;
}

输出结果:

value of area1 : 20

value of area2 : 15

const关键字:

const int var = 5;           //正确

const int var;                 //错误

const int var;

var = 5;                        //错误

存储类

这个面试过程中也经常被问到。

register存储类:用于定义存储在寄存器中而不是 RAM 中的局部变量。所以变量的访问速度更快,但是它不能直接取地址。

例如:

register int miles;

static 存储类:指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。

static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

例如:
 

#include <stdio.h>
 
/* 函数声明 */
void func1(void);
 
static int count=10;        /* 全局变量 - static 是默认的 */
 
int main()
{
  while (count--) {
      func1();
  }
  return 0;
}
 
void func1(void)
{              
  static int thingy=5;
  thingy++;
  printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}

实例中 count 作为全局变量只能在该文件下的函数内使用,thingy 使用 static 修饰后,不会在每次调用时重置。

输出结果:

thingy 为 6 , count 为 9

thingy 为 7 , count 为 8

thingy 为 8 , count 为 7

thingy 为 9 , count 为 6

thingy 为 10 , count 为 5

thingy 为 11 , count 为 4

thingy 为 12 , count 为 3

thingy 为 13 , count 为 2

thingy 为 14 , count 为 1

thingy 为 15 , count 为 0

extern存储类:定义在其他文件中声明的全局变量或函数。当使用 extern 关键字时,不会为变量分配任何存储空间,而只是指示编译器该变量在其他文件中定义。

例如:

第一个文件:main.c

#include <stdio.h>
 
int count ;
extern void write_extern();
 
int main()
{
   count = 5;
   write_extern();
}
第二个文件:support.c
#include <stdio.h>
 
extern int count;
 
void write_extern(void)
{
   printf("count is %d\n", count);
}

第二个文件中的 extern 关键字用于声明已经在第一个文件 main.c 中定义的 count。

输出结果:

count is 5

运算符

运算符包括有:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、杂项运算符

算术运算符:+、-、*、/、%、++、--

注意a++与++a的区别:

int c;

int a = 10;

c = a++;

printf("先赋值后运算:\n");

printf("Line 1 - c 的值是 %d\n", c );

printf("Line 2 - a 的值是 %d\n", a );

输出结果:

Line 1 - c 的值是 10

Line 2 - a 的值是 11

int c;

int a = 10;

c = ++a;

printf("Line 3 - c 的值是 %d\n", c );

printf("Line 4 - a 的值是 %d\n", a );

输出结果:

Line 3 - c 的值是 11

Line 4 - a 的值是 11

关系运算符:==、!=、>、<、>=、<=。最常应用于条件真假判断,if( A==B )

逻辑运算符:&&、||、!。也最常应用于条件真假判断,if( A&&B )

位运算符:&、|、^(异或运算符)

p q p & q p | q p ^ q

0 0 0 0 0

0 1 0 1 1

1 1 1 1 0

1 0 0 1 1

如果:

A = 0011 1100

B = 0000 1101

A & B = 0000 1100

A | B = 0011 1101

A ^ B = 0011 0001

~A = 1100 0011

赋值运算符:

运算符 例子 运算符 例子

= C=A+B <<= C<<=2相当于C=C<<2

+= C+=A相当于C=C+A >>= C>>=2相当于C=C>>2

-= C-=A相当于C=C-A &= C&=2相当于C=C&2

*= C*=A相当于C=C*A ^= C^=2相当于C=C^2

/= C/=A相当于C=C/A |= C|=2相当于C=C|2

杂项运算符:sizeof()、&、*、? :

例子:

#include <stdio.h>
 
int main()
{
   int a = 4;
   short b;
   double c;
   int* ptr;
 
   /* sizeof 运算符实例 */
   printf("Line 1 - 变量 a 的大小 = %lu\n", sizeof(a) );
   printf("Line 2 - 变量 b 的大小 = %lu\n", sizeof(b) );
   printf("Line 3 - 变量 c 的大小 = %lu\n", sizeof(c) );
 
   /* & 和 * 运算符实例 */
   ptr = &a;    /* 'ptr' 现在包含 'a' 的地址,&a给出变量的实际地址 */
   printf("a 的值是 %d\n", a);
   printf("*ptr 是 %d\n", *ptr);	//* ptr将指向一个变量,取值
 
   /* 三元运算符实例 */
   a = 10;
   b = (a == 1) ? 20: 30;
   printf( "b 的值是 %d\n", b );
 
   b = (a == 10) ? 20: 30;
   printf( "b 的值是 %d\n", b );
}

输出结果:

Line 1 - 变量 a 的大小 = 4

Line 2 - 变量 b 的大小 = 2

Line 3 - 变量 c 的大小 = 8

a 的值是 4

*ptr 是 4

b 的值是 30

b 的值是 20

运算符优先级

运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算,最经常出项选择题中。具体可以看看网上的例子。

判断语句

判断语言指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。

一般形式:

语句有:if语句、if…else语句、switch语句、

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

Exp1 ? Exp2 : Exp3;

? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个表达式的值。

C循环语句

循环语句允许我们多次执行一个语句或语句组。

语句有:while循环、for循环、do…while循环、嵌套循环(在 while、for 或 do..while 循环内使用一个或多个循环)

循环控制语句:

break语句(终止循环或 switch 语句。)

continue语句(一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。)

goto语句(将控制转移到被标记的语句。)

函数

每个C程序都至少有一个函数,即主函数 main()。

函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。

一般形式:

return_type function_name( parameter list )

{

body of the function

}

 返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。

 函数名称:function_name这是函数的实际名称。

 参数:parameter参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。

 函数主体:函数主体body of the function包含一组定义函数执行任务的语句。

例子:

int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
   if (num1 > num2) {
      result = num1;
   } else {
      result = num2;
   }
   return result; 
}

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

int max(int num1, int num2);

调用函数

当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。

例子:

#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; 
}

函数参数

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

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

当调用函数时,有两种向函数传递参数的方式:

传值调用:该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。

引用(指针)调用:通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

选择题经常考。

例子:

#include <stdio.h>

void swap(int x, int y);
void swap(int x, int y)
{
    int temp;
    temp = x;
    x = y;
    y = temp;
}

int main( int argc, char *argv[] )
{
    int a = 5;
    int b = 10;
    swap(a, b); //调用交换函数
    printf("交换结果为 a = %d, b = %d\n",a,b);
    return 0;
}

输出结果:交换结果为 a = 5, b = 10

由于值传递是单向传递,传递过程中只是改变了形参的数值,并未改变实参的数值,因此并不会改变a和b原有的值。

引用(指针)调用:

#include <stdio.h>

void swap(int *x, int *y);
void swap(int *x, int *y)
{
    int temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

int main( int argc, char *argv[] )
{
    int a = 5;
    int b = 10;
    swap(&a, &b); //调用交换函数
    printf("交换结果为 a = %d, b = %d\n",a,b);
    return 0;
}

输出结果:交换结果为 a = 10, b = 5

指针传递过程中,将a和b的地址分别传递给了x和y,在函数体内部改变了a、b所在地址的值,即交换了a、b的数值。

#include <stdio.h>

void swap(int &x, int &y);
void swap(int &x, int &y)
{
    int temp;
    temp = x;
    x = y;
    y = temp;
}

int main( int argc, char *argv[] )
{
    int a = 5;
    int b = 10;
    swap(a, b); //调用交换函数
    printf("交换结果为 a = %d, b = %d\n",a,b);
    return 0;
}

输出结果:交换结果为 a = 10, b = 5

引用传递中,在调用swap(a, b);时函数会用a、b分别代替x、y,即x、y分别引用了a、b变量,这样函数体中实际参与运算的其实就是实参a、b本身,因此也能达到交换数值的目的。

作用域规则

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

1、在函数或块内部的局部变量

2、在所有函数外部的全局变量

3、在形式参数的函数参数定义中

例子:

#include <stdio.h>
 
/* 全局变量声明 */
int a = 20;
 
int main ()
{
  /* 在主函数中的局部变量声明 */
  int a = 10;
  int b = 20;
  int c = 0;
  int sum(int, int);
 
  printf ("value of a in main() = %d\n",  a);
  c = sum( a, b);
  printf ("value of c in main() = %d\n",  c);
 
  return 0;
}
 
/* 添加两个整数的函数 */
int sum(int a, int b)
{
    printf ("value of a in sum() = %d\n",  a);
    printf ("value of b in sum() = %d\n",  b);
 
    return a + b;
}

输出结果:

value of a in main() = 10

value of a in sum() = 10

value of b in sum() = 20

value of c in main() = 30

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

这个知识点笔试会考到。

全局变量与局部变量在内存中的区别:

 全局变量保存在内存的全局存储区中,占用静态的存储单元;

 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。

这个知识点面试会考到。

数组

数组是一种数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。所有的数组都是由连续的内存位置组成。

数组中的特定元素可以通过索引访问,第一个索引值为 0。

例如:

int arr[10];

现在arr是一个可用数组,可以容纳10个类型为int得数字。

初始化数组

int arr[5]={1, 2, 3, 4, 5};

大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。

也可以这样初始化:

int arr[]={1, 2, 3, 4, 5};

如果要对某个元素赋值,则

arr[4] = 10;

上述的语句把数组中第五个元素的值赋为10。

深入了解数组:多维数组、传递数组给函数、从函数返回数组、指向数值的指针

枚举(enum)

定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。

选择题考点。

enum 枚举名 {枚举元素1,枚举元素2,……};

比如:一星期有 7 天,如果不用枚举,我们需要使用 #define 来为每个整数定义一个别名:

#define MON 1

#define TUE 2

#define WED 3

#define THU 4

#define FRI 5

#define SAT 6

#define SUN 7

如果使用枚举的方式:

enum DAY

{

MON=1, TUE, WED, THU, FRI, SAT, SUN

};

注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。

可以在定义枚举类型时改变枚举元素的值:

enum season {spring, summer=3, autumn, winter};

spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5

枚举变量的定义

1、先定义枚举类型,再定义枚举变量

enum DAY

{

MON=1, TUE, WED, THU, FRI, SAT, SUN

};

enum DAY day;

2、定义枚举类型的同时定义枚举变量

enum DAY

{

MON=1, TUE, WED, THU, FRI, SAT, SUN

} day;

3、省略枚举名称,直接定义枚举变量

enum

{

MON=1, TUE, WED, THU, FRI, SAT, SUN

} day;

例子:

#include <stdio.h>
 
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
 
int main()
{
    enum DAY day;
    day = WED;
    printf("%d",day);
    return 0;
}

输出结果:3

指针

每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。

#include <stdio.h>

int main ()
{
    int var_runoob = 10;
    int *p;              // 定义指针变量
    p = &var_runoob;
 
   printf("var_runoob 变量的地址: %p\n", p);
   return 0;
}

输出结果:var_runoob 变量的地址: 0063fec8

不同电脑输出地址不一样。

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

指针声明:

int *ip; /* 一个整型的指针 */

double *dp; /* 一个 double 型的指针 */

float *fp; /* 一个浮点型的指针 */

char *ch; /* 一个字符型的指针 */

所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。

指针的使用:

#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 变量的地址: 0063fec8

ip 变量存储的地址: 0063fec8

*ip 变量的值: 20

C 中的 NULL 指针

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

输出结果:

ptr 的地址是 00000000

指针深入了解:

指针的算术运算:可以对指针进行四种算术运算:++、--、+、-。

指针数组:可以定义用来存储指针的数组。

指向指针的指针:允许指向指针的指针。

传递指针给函数:通过引用或地址传递参数,使传递的参数在调用函数中被改变。

从函数返回指针:允许函数返回指针到局部变量、静态变量和动态内存分配。

函数指针与回调函数

函数指针是指向函数的指针变量。

例子:

#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    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;
}

声明了函数指针变量 p,指向函数 max。

输出结果:

请输入三个数字:1 2 3

最大的数字是: 3

回调函数

函数指针作为某个函数的参数

例子:

#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];
    /* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}

populate_array() 函数定义了三个参数,其中第三个参数是函数的指针。

回调函数 getNextRandomValue(),它返回一个随机值,它作为一个函数指针传递给 populate_array() 函数。

输出结果:

(随机10个数字)

C字符串

在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。

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

char site[6] = {'x', 'X', 'J', 'X', 'x', '\0'};

也可以把上面语句写为:

char site[] = {'x', 'X', 'J', 'X', 'x', '\0'};

字符串的内存表示:

索引

0

1

2

3

4

5

变量

x

X

J

X

x

\0

地址

0x2004

0x2005

0x2006

0x2007

0x2008

0x2009

字符串操作函数:

strcpy(s1, s2); 复制字符串 s2 到字符串 s1。

strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。

strlen(s1); 返回字符串 s1 的长度。

strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。

strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。

strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。

例子:

#include <stdio.h>
#include <string.h>
 
int main ()
{
   char str1[14] = "runoob";
   char str2[14] = "google";
   char str3[14];
   int  len ;
 
   /* 复制 str1 到 str3 */
   strcpy(str3, str1);
   printf("strcpy( str3, str1) :  %s\n", str3 );
 
   /* 连接 str1 和 str2 */
   strcat( str1, str2);
   printf("strcat( str1, str2):   %s\n", str1 );
 
   /* 连接后,str1 的总长度 */
   len = strlen(str1);
   printf("strlen(str1) :  %d\n", len );
 
   return 0;
}

C结构体

结构体定义由关键字 struct 和结构体名组成,结构体名可以根据需要自行定义。

语句格式:

struct tag {

    member-list

    member-list

    member-list 

    ...

} variable-list;

tag 是结构体标签。

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

variable-list 结构变量,定义在结构的末尾。

在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。以下为例子:

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c

//同时又声明了结构体变量s1

//这个结构体并没有标明其标签

struct

{

    int a;

    char b;

    double c;

} s1;

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c

//结构体的标签被命名为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;

};

结构体初始化:

#include <stdio.h>
 
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
 
int main()
{
    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}

输出结果为:

title : C 语言

author: RUNOOB

subject: 编程语言

book_id: 123456

访问结构体成员:成员访问运算符(.)

printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);

结构体作为函数参数:

void printBook( struct Books book );

……

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);

}

指向结构的指针

struct Books *struct_pointer;

为了查找结构变量的地址,请把 & 运算符放在结构名称的前面,如下所示:

struct_pointer = &Book1;

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

struct_pointer->title;

/* 函数声明 */

void printBook( struct Books *book );

……

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);

}

typedef

typedef 关键字,可以使用它来为类型取一个新的名字。例如:

typedef unsigned char BYTE;

在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:

BYTE b1, b2;

可以使用 typedef 来为用户自定义的数据类型取一个新的名字。例如,您可以对结构体使用 typedef 来定义一个新的数据类型名字,然后使用这个新的数据类型来直接定义结构变量,如下:

typedef struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} Book;

int main()
{
	Book book;
	……
}

typedef与#define

typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。

typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

#define知识点经常面试被问到,例如define与const的区别?

输入和输出

C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。

scanf() 函数用于从标准输入(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出(屏幕)。

%d 格式化输出整数

printf("Number = %d", testInteger);

%f 格式化输出浮点型数据

printf("Value = %f", f);

getchar() & putchar() 函数

int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。

int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。

例子:

#include <stdio.h>
int main( )
{
   int c;
   c = getchar( );
 
   putchar( c );
   printf( "\n");
   return 0;
}

输入:x

输出:x

gets() & puts() 函数

输入和输出字符串 s

例子:

#include <stdio.h>
 
int main( )
{
   char str[100];
   gets( str );
   puts( str );
   return 0;
}

输入:xXJXx

输出:xXJXx

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萧 十 三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值