C语言之初识C语言(点到为止,两万字文章详细解读!)(二)

简介: C语言之初识C语言



十、数组

/*这里只简单地概述一下一维数组的内容/

要存储1-10的数字,怎么存储?

C语言中给了数组的定义:一组相同类型元素的集合

1.数组的创建

# include<stdio.h>
int main()
{
   //数组的创建
   int arr[5]; //创建一个名为arr的整型数组,数组有5个元素, 每个元素为整型
   char ch[15]; //创建一个名为ch的字符数组,数组有15个元素,每个元素为字符型
  return 0;
}

2.数组的初始化

# include<stdio.h>
int main()
{
    //数组的初始化
    //在初始化变量时,我们一般不知道给什么元素,会暂时都赋值为0
    int a = 0;
    //1.同样,当我们在初始化的时候不知道给数组什么元素,我们可以将整型数组赋值为    {0},数组里的每个元素会被初始化为0
    int arr1[5] = {0};
    //2.在给定数组一些值时,中括号内规定多少元素,则最多只能存储多少个元素。
    int arr[5] = {1,2,3,4,5};
    //字符数组以下两种方法皆可
    char ch1[3] = "abc";
    char ch2[3] = {'a','b','c'};
  return 0;
}

查看数组的元素 [按f10调试->监视窗口]

2.73.png

#include<stdio.h>
int main()
{
  //数组的不完全初始化
  //给定若干元素,在赋值元素时,没有讲给定的元素全部填充,剩下的元素则会默认被初始化为0
  int arr[10] = {1,2,3,4,5};
  return 0;
}

2.8.png

3.数组的下标

C语言规定:数组的每个元素都有一个下标,下标是从0开始的。

数组可以通过下标来访问的。

比如:int arr[10] = {1};

如果数组有十个元素,其下标的范围为0 ~ 9

image.png

数组的元素需要通过数组的下标来访问。

# include<stdio.h>
int main()
{
   int arr[10] = {1,2,3,4,5,6,7,8,9,10};
   printf("%d",arr[4]);//访问数组的第五个元素,下标为4
  return 0;
}

遍历数组,将数组中的元素全部打印出来

#include <stdio.h>
int main()
{
 int i = 0;
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 for(i = 0; i < 10; i++)
 {
       printf("%d ", arr[i]);
 }
 printf("\n");
    return 0;
}

十一、操作符(运算符)

简单地初步认识就行,不作深入了解。

算数操作符:+    -     *    /     %

# include<stdio.h>
int main()
{
  int a = 5,b = 10 ,c;
   c = a + b; //15, +运算符作加法运算
   c = b - a; //5, -运算符作减法运算
   c = a * b; //50, *运算符作乘法运算
   c = b / a; //2, /运算符作除法运算
   c = b % 3; //1, %运算符作取模运算
  return 0;
}

%操作符只能作用于整数

# include<stdio.h>
int main()
{
  float m = 10.0f;
  float n = 3.0f;
  printf("%f\n",a / b); //3.333333  flaot类型默认打印小数点后六位
  //printf("%f\n",a % b); //错误的写法,求余运算只能用于整数
  float a = 10 / 3;//当/两边都为整数时,表达式执行的是整数除法,结果商为3余1,
  float b = 10.0f / 3;//如果要得到小数,必须保证/两边至少一个操作数是浮点数,结果才是3.3
  //10.0后加上f为float,不加f则默认为double类型
  printf("%f\n",a);//3.000000
  printf("%f\n",b);//3.333333
  return 0;
}

移位操作符:>>     <<

移动的是内存中的二进制位


位操作符:&     |    ^

按位与、按位或、按位异或


赋值操作符:=     +=     -=     *=     /==     &=     ^=     |=     >>=     <<=

# include<stdio.h>
int main()
{
   int a = 0; //初始化
     a = 10;// = 为赋值操作符
     a += 20 ; //可以看成 a = a + 20 ,即a+20得到的值再赋值给a,a的值为30
     //-=、/=、*=、%=同样可以这么理解
  return 0;
}

单目操作符:

 !             逻辑反操作

 -            负值

 +           正值

&           取地址

sizeof     操作数的类型长度(以字节为单位)

~            对一个数的二进制按位取反

–            前置、后置–

++         前置、后置++

*             间接访问操作符(解引用操作符)

(类型)     强制类型转换

加上!逻辑反操作把真变为假,把假变为真。

#include<stdio.h>
int main()
{
  int flag = 0;
  //在C语言中,0表示为假,非0表示为真
  //flag为真打印hehe
  if(flag)
  {
    printf("hehe\n");
  }
  //flag为假,打印haha
  if(!flag)
  {
  printf("haha\n");
  }
  return 0;
}

sizeof用来计算变量或者类型的大小

#include<stdio.h>
 int main()
{
   int a = 10;
   printf("%d\n",sizeof(a));//4 单位是字节
   printf("%d\n",sizeof(int));
   int arr[10] = {0};
   printf("%d\n",sizeof(arr));//计算的是数组的总大小,单位是字节
   printf("%d\n",sizeof(arr[0]));//计算数组首元素的大小
  int sz = sizeof(arr)/sizeof(arr[0]);//计算元素的个数
  printf("%d\n",sz);
  return 0;
}

自增(++)和自减(--

#include <stdio.h>
int main()
{ 
  //自增和自减用法一样
  //后置--
  int a = 10;
  int b = a--;//后置--,先试用再自减 即int b = a,a = a - 1;
  printf("a = %d b = %d",a,b);// a = 9 b = 10
  //前置--
  int a = 10;
  int b = --a;//前置--,先自减,后使用 即a = a - 1, int b = a;
  printf("a = %d b =%d\n",a,b);
  return 0;
}

()强制类型转换

#include <stdio.h>
int main()
{ 
  //一下两种写法都会发生警告
   int a = 3.14; //精度不够
   float f = 3.14; //默认为double类型,精度太大
  //这里用()强制类型转换操作符
  int a = (int)3.14;
  float f = (float)3.14;
  return 0;
}

关系操作符:

>

=

<

<=

!=         用于测试“不相等”

==        用于测试“相等”

逻辑操作符:

&&       逻辑与

||           逻辑或

逻辑操作符的运用

#include <stdio.h>
int main()
{
  /*输入一个字母,判断其是否为字母*/
  char input = ' ';
  scanf("%c",&input);
  //这里的&&就是与、并且的意思,而||是或者的意思
  if('a' < input && input < 'z' || 'A' < input && input < 'Z' )
  {
  printf("是字母");
  }else  
  printf("不是字母");
  return 0;
}

条件操作符(三目操作符):

exp1 ? exp2 : exp3

用法:当exp1为真就执行exp2的结果,为假则执行exp3的结果。

#include<stdio.h>
int main()
{
  /*输入两个值,求最大值*/
   int a = 0;
   int b = 0;
   int max = 0;
  scanf("%d %d",a,b);
  //if(a > b)
  //max = a;
  //else
  //max = b;
  /*也可以使用条件操作符(三目操作符)来表示*/
  max = (a > b ? a : b);
  printf("%d",max);
  return 0;
}

逗号表达式:

exp1, exp2, exp3,……expN

逗号表表达式的特点是:表达式从左向右依次计算,但是整个表达式的结果为最后一个表达式的结果。

#include<stdio.h>
int main()
{
   int a = 10;
   int b = 20;
   int c = 30;
   int d = (a += 10,b -= 15,c = a + b);
   printf("%d",d); //25
  return 0;
}

下标引用、函数调用和结构成员

[ ]  ()  .    ->


十二、 常见关键字

C语言中提供了32个关键字,而这些关键字是C语言中已经预先设定好的,我们用户自己不能创造关键字。


auto  break   case   char   const   continue  default  do   double  else  enum   extern  float  for   goto   if  int   long   register   return   short  signed  sizeof   static  struct  switch typedef   union unsigned   void  volatile  while


循环语句中用到的关键字:


break、continue、do……while、while、for


分支语句关键字:


switch、case、if……else、goto


函数中的返回值:


return


变量中常用到的:


auto、register、static


关键字auto和register

#include<stdio.h>
int main()
{
  auto int a = 10;//局部变量(自动变量),自动变量,自动销毁
  register int num = 100;//建议把100放在寄存器中存储
  return 0;
}

关键字static

static是用来修饰变量和函数的

  1. 修饰局部变量-称为静态局部变量
  2. 修饰全局变量-称为静态全局变量
  3. 修饰函数-称为静态函数

1、修饰局部变量

 #include<stdio.h>
 void test()
 {
  int a = 1;
  a++;
  printf("%d ",a);   // 此处代码打印为(10个2)2 2 2 2 2 2 2 2 2 2  
}
 int main()
{
   int i = 0;
   while(i < 10)
   {
    test();
    i++;
  }
  return 0;
}

在局部变量a的前面加上static结果就不一样了

 #include<stdio.h>
 void test()
 {
  static int a = 1; //静态变量
  a++;
  printf("%d ",a);   // 此处代码打印为(2到11)2 3 4 5 6 7 8 9 10 11  
}
 int main()
{
   int i = 0;
   while(i < 10)
   {
    test();
    i++;
  }
  return 0;
}

为什么呢?

在内存中,主要分为三个区域,栈区、堆区和静态区域。栈区存放局部变量、形式参数,堆区主要用于动态内存分配,而静态区主要存放静态变量和全局变量。

image.png

一个普通的局部变量是存放在栈区的,而被static修饰的局部变量,是存放在内存的静态区的,本质上改变了在内存中的存储位置,使得局部变量出了作用域不会销毁,影响了变量的生命周期(作用域不受影响)。


2、修饰全局变量

再新建一个add.c的源文件,写入一个全局变量g_val

3.1.png

我们发现,直接使用会有报错显示“未声明的标识符”,此时在test.c文件中声明一下g_val,就可以运行了3.2.png

全局变量是具有外部链接属性的,在其他源文件内部,只要适当地申明就可以使用。

当我们用static去修饰这个全局变量的时候,它就会报错(无法解析的外部符号),这是因为static修饰的全局变量的外部链接属性消失了,而变成了内部链接属性,只能在自己的.c文件内部使用,其他.c文件内部无法使用。所以本质上是影响了变量的作用域范围


3.3.png3.4.png

3.4.png

//代码1
//add.c
int Add(int x, int y)
{
    return c+y;
}
//test.c
//声明外部符号 -函数
extern int Add(int , int);
int main()
{
    printf("%d\n", Add(2, 3));
    return 0;
}

static修饰函数后,函数的外部链接属性就变成了内部链接属性,被static修饰的函数只能在自己的.c文件内部使用,其他.c文件内部无法使用,本质上影响了作用域范围。

//代码2
//add.c
static int Add(int x, int y)
{
    return c+y;
}
//test.c
//声明外部符号 -函数
extern int Add(int , int);
int main()
{
    printf("%d\n", Add(2, 3));
    return 0;
}

类型:

union、enum、struct、char、short、int、float、long、double、const、void、sizeof、signed、unsigned、typedef

关键字typedef—类型重命名

#include<stdio.h>
//将unsigned int 重命名为uint, 所以uint也是一个类型名
typedef unsigned int uint;
 int main()
{
   //观察num1和num2,这两个变量的类型是一样的
  unsigned int num1 = 100;
  unit num2 = 200; 
  return 0;
}

十三、define定义的常量和宏

1.定义常量

可以使用#define定义一个标识符来表示一个常量

注:由define定义的常量只能在定义的地方做修改,其他处无法被修改。

格式:#define 标识符 常量值

#include<stdio.h>
//因为不属于语句,所有后面不需要添加分号
#define MAX 10000  //定义整型常量MAX 其值为10000
#define char_str "Hello,world" //定义字符串常量char_str 其值为"Hello,world"
int main()
{
  printf("%d",MAX);//10000
  printf("%s",char_str);//Hello,world
return 0;
}

2.定义宏

也可以使用#define来定义宏

#include<stdio.h>
 //定义宏与定义常量的差异在哪里呢?
 //定义宏需要给予参数,而定义常量是没有参数的
 //宏类似于函数,但是没有参数的数据类型
 #define max(x,y) ((x)>(y)?(x):(y)) //这里的x,y为一个参数,可加可不加。
                  //但是若x或y为一个表达式时,括号就必须加上,保证其准确性。
int main()
{
  int a = 10;
  int b = 20;
  int m = max(a,b); //编译器会自动替换为 int m = (a>b?a:b);
  printf("%d",m);
  return 0;
}

十四、指针

在学习指针之前,我们首先得认识认识一下内存。


内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。

所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。

为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。


假设有一栋楼,该楼共有六层,共有三十层房间,在没有门牌号的情况下,如何精准地定位找到自己想去的房间?不好寻找。而在实际生活中,每个房间都有门牌号,这让我们直接就可以找到自己想去的房间,而不至于那么麻烦。

image.png

而在内存中,内存中的每一个内存单元相当于实际生活中的每一个房间,每一个内存单元也有对应的一个编号,而通过这个编号,我们也能快速找到这样一个个小的内存单元,这一个个内存单元的编号被称为地址,也叫做指针

所以我们有这样一概念( 编号 = 地址 = 指针)。

image.png

我们想一下,1个内存单元给多大比较合适呢?

1个字节!

为什么要1个字节,而不是1bit、1KB……


如果用char类型来说,如果内存单元为1bit的话,需要8个bit的内存空间,而int类型则需要4*8=32个内存空间,显然,用bit多余了,1KB又太大,而1个字节恰恰合适。

基本的数据类型char类型占1个字节,int类型占4个字节。所以每个内存单元的单位以1个字节最合适。


那么内存单元的编号(地址)是怎么产生的呢?


在32位机器上,有32根地址线(物理电线),电线是通电的,通电之后就有高电平和低电平之分,电信号最终会转换成数字信号,而高电平转换成数字信号为数字“1”,低电平转换成数字信号为数字“0”,由此会产生32个0或者1的随机数字信号,就会组成232个二进制序列,这些组成不同的二进制序列就可以作为内存单元的编号,也就会产生232个地址。


那么232个地址就能管理232个字节的空间,即4294967296个byte,

4294967296byte/1024 = 4194304KB;

4194304KB/1024 = 4096MB;

4096MB/1024 = 4GB;

所以,232个地址最多也就占用4GB的大小内存。


int a = 10,本质就是在内存中申请4个字节大小的空间来存储10。


image.png

image.png

如何取到变量a的地址呢?

用&a(&为取地址操作符)表示变量a的地址。

注:a实际占用4个字节的空间,每个字节都有地址,但是&a拿到的是第一个字节的地址编号(也是最小的地址,如图中:0x0012ff40);


&a拿出的是地址,地址也是一个值,如何来存储&a呢,用int* pa = &a来存放地址,此时的pa是一个存放地址(指针)的变量,故pa被称为指针变量


image.png

image.png

那么pa仅仅是用来存储地址的吗?其实不然,我们可以通过pa来找到a,用*pa来表示。(此时的*为解引用操作符)

#include <stdio.h>
int main()
{
  int a = 10;
  int* pa = &a;
  printf("%p\n",&a);//%p为打印地址格式符
  printf("%d\n",a);//10
  *pa = 20;//此时a的值被覆盖为20
  printf("%d\n",a);//20
 return 0;
}

除了整型类型的指针之外,其他类型如字符类型的指针,例子如下:

#include <stdio.h>
int main()
{
   char ch = 'A';
   char* pc = &ch;
   *pc = 'q';
   printf("%c\n", ch);
 return 0;
}

指针变量的大小是多大呢?


指针变量是用来存放地址的,地址在32位机器上,是由32个0或1组成的二进制序列号,故需要32个比特位的空间存储,指针变量就需要四个字节,不论什么数据类型的指针变量,大小都是4个字节。

在64位机器上,是由64个0或者1组成的二进制序列号,需要64个比特位的空间存储,指针变量的大小就是8个字节。


结论:指针大小在32位平台是4个字节,64位平台是8个字节


我们也可以通过sizeof来计算指针变量的大小

#include <stdio.h>
int main()
{
  //x86(32)位平台上指针变量的大小为4个字节
  int* pa;
  char* ch;
  double* pd;
  printf("%d\n", sizeof(pa)); //4
  printf("%d\n", sizeof(ch)); //4
  printf("%d\n", sizeof(pd)); //4
  return 0;
}

十五、结构体

结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。

比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。

描述复杂对象,不能简单的使用单个的内置类型来描述了,这里只能使用结构体来描述。

例如:

struct Student
{
    char name[20];//名字
    int age;      //年龄
    char sex[5];  //性别
    char id[15]; //学号
};

结构体的初始化:

 int main()
 {
  struct Student s1 = {"张三",18,"男","12345678"};
  //直接打印结构体成员
  printf("%s %d %s %s\n",s1.name,s1.age,s1.sex,s1.id); //结构体变量.成员
  struct Student * ps = &s1;//指针结构体
  printf("%s %d %s %s\n",(*ps).name,(*ps).age,(*ps).sex,(*ps).id);
  printf("%s %d %s %s\n",ps->name,ps->age,ps->sex,ps->id); //结构体指针->成员
  //struct Student s2 = {"李四",18,"女","14725836"};
  return 0;
  }

/*以上为初步学习C语言知识部分(点到为止不做深度解读),已完结 。*/


目录
相关文章
一篇文章让你看懂C语言字符函数和内存函数(上)
一篇文章让你看懂C语言字符函数和内存函数(上)
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
361 7
|
存储 C语言
【C语言基础】一篇文章搞懂指针的基本使用
本文介绍了指针的概念及其在编程中的应用。指针本质上是内存地址,通过指针变量存储并间接访问内存中的值。定义指针变量的基本格式为 `基类型 *指针变量名`。取地址操作符`&`用于获取变量地址,取值操作符`*`用于获取地址对应的数据。指针的应用场景包括传递变量地址以实现在函数间修改值,以及通过对指针进行偏移来访问数组元素等。此外,还介绍了如何使用`malloc`动态申请堆内存,并需手动释放。
361 9
|
存储 机器学习/深度学习 编译器
一篇文章,把你的C语言拉满绩点
一篇文章,把你的C语言拉满绩点
83 0
|
存储 程序员 C语言
揭秘C语言:这些核心知识你掌握了吗?一篇文章带你突破编程基础,开启高效编码之旅!
【8月更文挑战第22天】C语言作为编程基石,以简洁高效著称,历经数十年仍备受欢迎。本文通过梳理C语言的核心概念,帮助读者深入理解并提升技能。适合各水平读者。基础语法从`main`函数开始,如示例中的“Hello, World!”程序所示。C语言强调头文件包含与语句结尾的分号。变量和数据类型丰富多样,如`int`、`float`、`char`等,合理选择可优化内存使用和性能。指针用于间接访问内存,是C语言的关键特性。控制结构如循环和分支使程序逻辑更灵活。函数支持代码复用与模块化。深入学习还需掌握预处理指令、文件操作等高级特性。通过系统学习与实践,你将能更熟练地使用C语言,构建高效稳定的应用。
230 4
|
编译器 Go C语言
一篇文章让你明白C语言中的分支与循环语句(一)
一篇文章让你明白C语言中的分支与循环语句
199 0
|
C语言
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
121 0
|
C语言
【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)
【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)
124 0
|
存储 编译器 C语言
【C初阶——指针3】鹏哥C语言系列文章,基本语法知识全面讲解——指针(3)
【C初阶——指针3】鹏哥C语言系列文章,基本语法知识全面讲解——指针(3)
160 0
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的C语言在线评测系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的C语言在线评测系统附带文章源码部署视频讲解等
143 0