初识C语言_Part 2(零基础超详解!)(一)

简介: 初识C语言_Part 2(零基础超详解!)

初识C语言 (2)


#1. 关键字


前言:C语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,用户自己是不能创造关键字的。


1.1static关键字


1.1.1 static修饰局部变量


我们来看下边一段示例代码:


void test()
{
  int a = 5;
  a++;
  printf("%d ", a);
}
int main()
{
  int i = 0;
  while (i < 10)
  {
  test();
  i++;
  }
  return 0;
}


这段代码会打印10个6,为什么呢?因为a这个变量是定义在test这个函数内部的,是个局部变量。每次调用test函数时,a变量会被重新创建并且被赋初始值为5,再经过++操作变成6被打印,当test函数调用完毕时,a这个局部变量又会被销毁。当下一次调用时,a又会被重新创建并被赋初始值为5,这样一直重复10次。


假若我们用static修饰a变量呢?


void test()
{
  static int a = 5;
  a++;
  printf("%d ", a);
}
int main()
{
  int i = 0;
  while (i < 10)
  {
  test();
  i++;
  }
  return 0;
}


此刻,就会在屏幕上打印6-15.


原因:被static修饰的局部变量,该变量的空间直到整个程序运行结束(main函数执行完毕)才会被销毁。当第一次调用test函数时,a被创建赋初始值为5,++之后变成6被打印,但是这次结束调用时,a变量的空间并不会被销毁,并且a的值为6,当第二次调用test函数时a的值仍然为6,并且a的地址不会发生变化,经过++操作之后,a变为7……以此重复10次。


注意:被static修饰的局部变量在编译阶段就被创建了。


但是为什么被static修饰之后,局部变量的空间就变得“长期有效”了呢?


解释:在c/c++中,内存空间被大致分为了三部分:


栈区:存放局部变量,形参,以及 具有临时作用的变量,栈区变量的特点是,进入作用域就创建,出了作用域就被销毁。

堆区:用于动态内存分配的(malloc,realloc,calloc)

静态区:存放全局变量,静态变量,静态区的变量的特点是:创建之后,直到程序结束才被销毁


所以:局部变量本来是存放在栈区的,但是被static修饰的局部变量就被放在了静态区了,static修饰局部变量,改变了变量的存储类型,使局部变量的生命周期变长了,但是作用域不变,直到程序结束才结束。


1.1.2 static修饰局部变量


我们在一个程序中创建两个源文件:


test1.c


int g_val = 10;


test2.c


#include<stdio.h>
extern int g_val;
int main()
{
  printf("%d", g_val);//打印10
  return 0;
}


假如test1.c中的g_val被static修饰呢?那么程序将报错无法运行。


因为:全局变量具有外部链接属性,所以可以在其他的源文件内使用。static修饰全局变量时,改变了全局变量的链接属性,由外部链接属性变成了内部链接属性。所谓内部链接属性,就是只能在自己内部源文件能看到。这个静态变量只能在自己的源文件内部使用,不能在其他的源文件内使用了 。


1.1.3 static修饰函数


test1.c


int Add(int x, int y)
{
  return x + y;
}


test2.c


extern int Add(int x, int y);
int main()
{
  int a = 10;
  int b = 10;
  int sum = Add(a, b);
  printf("%d", sum);//打印20
  return 0;
}

若Add函数被static修饰,那么该函数只能在该源文件内部使用。


因为:static修饰函数,函数本身也具有外部链接属性,被static修饰之后,就变成了内部链接属性。使得函数只能在自己所在的源文件内部使用,不能在其他文件内使用。


1.2.struct关键字


当我们想要用C语言描述一个学生时,单靠C语言本身的数据类型很难做到。C语言本身提供的类型有点单一,所以这时就可以自定义一个类型,可以用struct关键字来定义。例如:


struct Student
{
  char name[10];
  int age;
  char sex[5];
};


此时我们就创建了一个结构体类型,结构体中的name,age,sex称之为结构体成员。struct Student称为结构体类型,这个struct Student 是自定义类型,int是C语言自带的类型,他们的用法类似,都能创建变量。接下来我们利用struct Student 创建一个结构体变量。如下:


int main()
{
  struct Student student = { "张山",19,"男" };
    struct Student student1 = { "李四",17,"男" };
  return 0;
}


此时我们创建了学生student和student1,我们可以这样理解,struct Student就相当于是个房间设计图纸,通过这个图纸我们设计出很多房间。在创建结构体的同时,我们对结构体成员进行初始化。注意:初始化时要和结构体的成员相对应,例如:张山对应char name[10]; 19对应int age;男对应char sex[5];


那么我们如何访问这些结构体成员呢?这里就要提到之前的 . (结构体成员访问符),下面我们使用这个操作符打印一下结构体成员:


struct Student
{
  char name[10];
  int age;
  char sex[5];
};
int main()
{
  struct Student student = { "张山",19,"男" };
  struct Student student1 = { "李四",17,"男" };
  printf("%s %d %s", student.name, student.age, student.sex);//打印结果: 张山 19 男
  return 0;
}


1.3 typedef关键字


定义:typedef是类型重定义符号。可以为一个类型重命名。看示例代码:


注意:typedef是类型重定义,例如下边这段代码,被重定义之后的用法和重定义之前的用法是一样的。


unsigned long long a = 10;
int main()
{
  unsigned long long b = 20;
  printf("%d %d", a,b);
  return 0;
}


我们在写这段代码时,会发现unsigned long long这个类型名字很长,为了方便节省时间,我们通常对其进行类型重定义操作。例如:


typedef unsigned long long ull;//被重定义之后,ull也是一个类型名
unsigned long long a = 10;//这里a和b的类型是一样的
int main()
{
  ull b = 20;
  printf("%d %d", a,b);
  return 0;
}


改成这样就会方便很多。注意:typedef使用完之后要在语句之后加分号。(就像第一行这样,分号不能少)。


1.4 define定义常量和宏


1.4.1 define定义常量


我们看之前的文章举过的一个例子:


//编译失败 报错
int main()
{
  const int a = 10;
  int arr[a] = { 0 };
  return 0;
}

这段代码会报错,注意:数组的括号内必须是常量,因为被const修饰的变量本质上还是变量。那么这里提到的define关键字就可以定义真正的常量。请看代码:


这里特别注意:c99标准之前,数组的大小不能是变量。但在c99标准中引入了变长数组的概念,这时允许数组的大小是变量,但是不能直接被初始化,但是visual studio编译器不支持变长数组。但是有些编译器是支持的,例如:gcc编译器就支持c99中的变长数组。


注意:使用define时,前面的#不要忘了哟


//成功运行
#define MAX 100
int main()
{
  int arr[MAX] = { 0 };
  return 0;
}

这里的由#define定义的就是常量。#define不仅仅能定义数字,例如:


//成功运行
#define NAME "张山" //define定义字符串
int main()
{
  printf("%s", NAME);//注意以%s格式打印字符串
  return 0;
}


当然还能定义其他的类型,这里就不赘述了。


1.4.2 define定义宏


回忆一下之前讲过的函数部分,我们提到了Add函数,能完成两个数的相加,下面我们就用宏来实现类似的相乘的功能。


//语法格式 #define 宏名(宏参数)宏体
#define ADD(x,y) ((x)*(y))
int main()
{
  int a = 10;
  int b = 20;
  int sum = ADD(a+b, b);
  printf("%d", sum);
  return 0;
}


可以自己感受一下宏的用法,这里建议((x)*(y))这里分别给x和y的括号不要省略了,假若我们去掉,测试一下:


#define ADD(x,y) (x*y)
int main()
{
  int a = 10;
  int b = 20;
  int sum = ADD(a+b, b);
    //编译的时候会替换成: int sum = (a+b*b) 不是我们想要的运算顺序和运算结果
  printf("%d", sum);
  return 0;
}


我们会发现,这里就得不到我们想要的结果了,因为宏只是简单字面替换。


相关文章
|
C语言
初识C语言_Part 2(零基础超详解!)(二)
初识C语言_Part 2(零基础超详解!)
|
存储 编译器 C语言
初识C语言_Part 1(零基础超详解!)(二)
初识C语言_Part 1(零基础超详解!)
109 0
|
C语言
初识C语言_Part 1(零基础超详解!)(一)
初识C语言_Part 1(零基础超详解!)
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
33 3
|
6天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
21 6
|
26天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
34 10
|
19天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
25天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
53 7
|
25天前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
29 4
|
30天前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。