初识C语言(下)

简介: 初识C语言(下)

前言


本章将介绍C语初级的余下部分。

主要包括了操作符,常见关键字,关键字中又重点介绍了static、typedef、#define 定义常量和宏、指针和结构体。


一、操作符


对于部分的操作符号不过多讲解,以后遇到在解释。


对于操作符我们也是分类的,首先介绍的第一类就是算术操作符。


和数学中的操作符很像,就是加减乘除等于。+、-、/、*(×),计算机语言中都适用。c语言中一个等号(=)并不表示等于,而是表示赋值,两个等号才能表示等于(==)。


例如:

float a=1+2;
float b=1*2;
float c=5/2;
float d=2-1;

其他都没有什么多解释的,唯一需要注意的只有除法。

看看编译器的结果


7b280abec03a4ea489927e3f2ca3c4bc.png


它并不是我们所想的那样结果为2.50000,而是为2.00000。这是为什么呢?


在c语言中定义“/”左右两边都为整数是计算的结果就为整数,所以结果为2.5后转换为int类型后就是2,因为结果又为float,所以为2.00000。


。如果将左右两边的int换成float结果就和我们想的一样了。


7886b45fdfec4d20bbdbcbbf6f6313ad.png


位移操作符:


<<(左移)和>>(右移),移位运算符组成的表达式也属于算术表达式,其值为算术值。左移运算是将一个二进制位的操作数按指定移动的位数向左移动,移出位被丢弃,右边移出的空位一律补0,右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位一律补0,或者补符号位,这由不同的机器而定。在使用补码作为机器数的机器中,正数的符号位为0,负数的符号位为1。


以后我们还会专题见位移操作符,不用纠结在这里。


位操作符:


&、|、^、~

& 按位与

| 按位或

^ 按位异或

~取反

这里不做讲解


赋值操作符:


=  +=  -=  *=  /=  &=  ^=   |=    >>=   <<=

这些就是复合运用,很好理解。就用+=来举例。

a+=2就是说a=a+2,把原来的a加上2在赋值给a。

其他的与之同理,不一 一介绍。


单目操作符:


顾名思义,一个符号表示的操作符,就叫单目操作符。


逻辑反操作
- 负值
+ 正值
& 取地址
sizeof                         操作数的类型长度(以字节为单位)
~   对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型)   强制类型转换


我们知道了==表示判断相等,由于C没有bool类型所以C使用了相等返回1表示为真,不相等返回0,表示为假。


那么如何判断为不等于呢,C提供了逻辑反操作:!


a!=b,表示判断a是否不等于b。返回类型与==一样。


- 、+ 这就和数学中的用法一样。


&取地址操作符,我在这里简单讲一下地址。我们C的内存位置分为三类,栈区,堆区,静态区。栈区主要存放一些变量等,堆区存放一些malloc等我们动态分配的内存等,而堆区主要存放static修饰的量和常量等。我们一旦创建一个量就需要给该量分配一个地址用于存放该量。我们给每个内存都赋予了一个编号,用于管理等等。这个地址不是我们主动分配的,而是由机器决定。并且我们的内存是有限的有时需要考虑内存问题。


在生活中,我们讨论一个计算机是32位,64位机器,那这些32位,64位是什么意思。32位机器有32根地址线,地址线是物理线,它可以通电,通电的时候它的正电就是1,负电就为0。有正负电之分就产生了1/0这样的信号。我们把电信号转换为数字信号,这个时候就组成了由1和0组成的二进制序列。那32根地址线产生的二进制序列就有32个全0到32个全1,2^32个二进制序列。当一个这样的二进制序列成为一个内存编号的时候,我们就把这个编号称为这个内存的单元的地址。


而这个&就是用于访问地址的。

siziof之前有提到过,这就不再继续提了。


-- 、++又叫做自增、自减运算符。


举例说明:


a++就是说a=a+1;++a也是如此。但是二者还是有区别的。

a++;//后置++表示,先对a进行预算,在自增一次
++a;//先对a自增一次,以后再进行运算


不要随意使用自增自减运算符,这里会出现很多错误。比如b+++a,计算机不能识别你是b+ 一个自增以后的a还是要b自增+a呢。还有就是(a++)+(a++)+(a++)像这种代码会有很大问题的,这个代码在不同的编译器下造成的结果是不一样的。所以这种代码千万不要写出来。


自减运算同理。


(类型)表示强制类型转换 例:

float num=2;
int b=(int)a;//这里的a被强制转换为了int类型


关系操作符


c没有数学中的大于等于号,但C提供了其他表示方法 >   >=    <     <=     !=    ==

很简单,不需要多介绍。


逻辑操作符


&&     逻辑与

||         逻辑或

&&必须左右两个边同时为真才为真,||只需要一边为真即为真。

注意一点小细节,&&如果左边不为真的话右边计算机就不再继续执行了。


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


exp1 ? exp2: exp3

exp:expression表达式。判断表达式1是否成立,如果为真即输出表达式2,假即输出表达式2。

表达式可以为任意表达式,我们运用的非常广泛。


逗号表达式


逗号表达式,它将两个及其以上的式子联接起来,从左往右逐个计算表达式,整个表达式的值为最后一个表达式的值。


如:(3+5,6+8)称为逗号表达式,其求解过程先表达式1,后表达式2,整个表达式值是表达式2的值。


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


下标引用不用过多介绍,数组里面常常会用到。符号[]。


函数调用就是main()中的符号()。


结构体成员访问操作符 “.”  “  ->”,等到讲解结构体会提到的。


二:常见关键字


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

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

我们在这里只能简单讲解几个,等以后遇见一个讲解一个。这里面有很多之前提到过的,if,double,char,const,等等。

本章重点介绍typedef和static。

typedef:


顾名思义是类型定义,这里应该理解为类型重命名。

例如:

//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main()
{
    //观察num1和num2,这两个变量的类型是一样的
    unsigned int num1 = 0;
    uint_32 num2 = 0;
    return 0;
}


static:


在C语言中:


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

1. 修饰局部变量-称为静态局部变量

2. 修饰全局变量-称为静态全局变量

3. 修饰函数-称为静态函数

static翻译过来就是静态的,被static修饰过的量都存放在静态区。在讲解static之前先介绍一下变量的创建与销毁。auto—自动的,我们之前有了解过量出了定义域以后就会自动销毁,同样,在量进入时会自动调用auto为量分配内存,而我们不需要去调用它,计算机自动调用。


而被static修饰后,量哪怕是出了定义域了都不会被销毁,只有当程序结束时,他才会销毁。


1.修饰局部变量:


例:

#include <stdio.h>
void test()
{
    int i = 0;
    i++;
    printf("%d ", i);
}
int main()
{
 int i = 0;
    for(i=0; i<10; i++)
   {
        test();
   }
    return 0; 
}

b7fd6be36a6c4fb998ffde7d86a76bb0.png


每一次调用test()函数就创建一个新的i,函数结束i就被销毁。  

这是变量没有被static修饰的结果。


#include <stdio.h>
void test()
{
    //static修饰局部变量
    static int i = 0;
    i++;
    printf("%d ", i);
}
int main()
{ int i = 0;
    for(i=0; i<10; i++)
   {
        test();
   }
    return 0;
}

ea02e4be835b40fcb3bcd8d169599dbe.png


test()函数只创建一个i,知道整个程序结束时i才会被销毁。

总结:static修饰局部变量改变了变量的生命周期,让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。

2.修饰全局变量:


//add.c
int g_val = 2018;
//test.c
int main()
{
    printf("%d\n", g_val);//g_val定义在不同的文件里
    return 0; 
}
结果输出为2018
//add.c
static int g_val = 2018;
//test.c
int main()
{
    printf("%d\n", g_val);
    return 0; 
}
这里会出现编译异常。g_val未被定义


这是因为,g_val被static修饰过后只允许在本文件中使用,不允许其他文件调用。


结论:一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使。


3.修饰函数

例:

//add.c
int Add(int x, int y) {
    return c+y; 
}
//test.c
int main()//分别定义在两个不同的源文件中
{
    printf("%d\n", Add(2, 3));
    return 0;
}
结果输出为5

比较被static修饰的结果

//add.c
static int Add(int x, int y) {
    return c+y; 
}
//test.c
int main()
{
    printf("%d\n", Add(2, 3));
    return 0;
}

与之前的一样,编译器会报错。


总结:一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。


三:#define 定义常量和宏

我们的常量除了之前定义方法以外,还有其他的定义方法。就是这里的#define修饰

#define NUM 50


并且写在代码的第一行,由于它是个常量,我们一般将他的所有字母大写。

宏是用于定义函数的,宏的定义同样也是需要写在第一行,但是也是有区别的,我们所定义的宏是有参数的。例:

//define定义宏
#define ADD(x, y) ((x)+(y))

(x,y)叫做宏的参数,((x)+(y))叫做宏体。


#define ADD(x, y) ((x)+(y))
#include <stdio.h>
int main()
{
    int sum = ADD(2, 3);
    printf("sum = %d\n", sum);
    sum = 10*ADD(2, 3);
    printf("sum = %d\n", sum);
    return 0; 
}
结果为 5 50


其实宏和函数还是特别像的,不同在于实现的定义不同,宏的参数是不需要参数类型的,而函数缺少参数则不行。

至于为什么宏体内部的x,y需要加括号,是因为他们是一个整体,可以为表达式,所以需要用括号括起来。


四:指针


1.内存


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

所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是 1 个字节 。 为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地 址 。

示意图:


内存 地址
一个字节 0X1122
一个字节 0X1133
.... ....
一个字节 0X0000


变量是创建内存中的(在内存中分配空间的),每个内存单元都有地址,故变量也是有地址的。

取出变量地址如下:

#include <stdio.h>
int main()
{
 int num = 10;
 &num;//取出num的地址
    //注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
 printf("%p\n", &num);//打印地址,%p是以地址的形式打印
 return 0; 
}


3783f76db79b4c28a1b3bc1e230b5685.png

结果如上。至于为什么会有EF是因为计算机这里是使用十六进制储存地址的。

那么问题又来了,我们需要如何储存该地址呢?所以就提出了定义指针变量。

int num = 10;
int *p;//p为一个整形指针变量
p = &num;

p是个变量,用于储存num的地址。

#include <stdio.h>
int main()
{
 int num = 10;
 int *p = &num;
 *p = 20;
    return 0; 
}

990b0ea38f4041d28ab22dce5b54b3ae.png


当然每个类型都有一个对应的指针类型

char ch='m';
char* pc = &ch;
----------------
double db=546.5;
double* pc=&db;

*是指针的意思,也称之为解引号。指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。


2.指针变量的大小:


指针变量的大小取决于地址的大小 ,如果是32位平台下地址是32个bit位(即4个字节)

若是64位平台下地址是64个bit位(即8个字节)。可以通过计算看看具体的值。


int main()
{
    printf("%d\n", sizeof(char *));
    printf("%d\n", sizeof(short *));
    printf("%d\n", sizeof(int *));
    printf("%d\n", sizeof(double *));
    return 0;
}


cf696d70fb184a7683c12aa75e19e942.png


其实这里不该用%d,%d只是用来取整数的,这里改用%zd。

%zd是强制转化为整形的格式输出符,对应的是size-t类型的(c99)中规定 ,不过有些编译器可能不支持%zd,它可能只支持%u或者%lu。


五:结构体


我们的数组可以用于储存同类型的数据,而我们的结构体可以用于储存不同类型的数据,一个结构体内部可以含有很多种类型,同时可以为数组。


在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据。结构体的定义形式为:


struct 结构体名{

   结构体所包含的变量或数组

};


例如:描述一个学生,该学生的名字,学号,年龄,性别,等等。

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

注意大括号后面的分号;不能少,这是一条完整的语句。


结构体也是一种数据类型,它由程序员自己定义,可以包含多个其他类型的数据。


像 int、float、char 等是由C语言本身提供的数据类型,不能再进行分拆,我们称之为基本数据类型;而结构体可以包含多个基本类型的数据,也可以包含其他的结构体,我们将它称为复杂数据类型或构造数据类型。


对成员的初始化:

#include <stdio.h>
struct Stu
{
    char name[20];//名字
    int age;      //年龄
    char sex[5];  //性别
    char id[15]; //学号
};
int main()
{
    struct Stu s = {"小吴", 20, "男", "202146535"};
    return 0;
}

除了这种整体初始化以外,还可以逐个初始化

  1. stu.name = "Tom";
  2. stu.age = 18;


结构体成员的获取:


结构体和数组类似,也是一组数据的集合,整体使用没有太大的意义。对于数组,我们使用操作符 [ ] 来获取,其中的某一个元素,对于结构体,我们使用 “  .  ”或者“ ->”来获取结构体中的某个成员,我们将其称之为结构体成员访问操作符。


同样是结构体访问操作符,二者还是有些区别的:


“ . ”操作符左边的操作数是一个“结构体”的表达式,而“ -> ”操作符左边的操作数是一个指向结构体的指针。


例:


struct Stu s = { " 张三 " , 20 , " 男 " , "20180101" };
//. 为结构成员访问操作符
printf ( "name = %s age = %d sex = %s id = %s\n" , s . name , s . age , s . sex , s . id );
//-> 操作符
struct Stu * ps = & s ;
printf ( "name = %s age = %d sex = %s id = %s\n" , ps -> name , ps -> age , ps -> sex , ps -
> id );

为了使用方便和直观,C语言允许把(*s).name用ps->name来替换,也就是说(*s).name和ps->name是等价的。


所以在结构体中“ . ”和“ -> ”的用法相似,但是并不等价。

相关文章
|
Linux 编译器 C语言
C语言必知必会
C语言必知必会
80 0
|
6月前
|
存储 人工智能 编译器
C语言:练习5
C语言:练习5
54 2
|
存储 编译器 C语言
初识C语言(四)
初识C语言(四)
|
6月前
|
Java 程序员 编译器
什么是C语言
什么是C语言
126 0
|
6月前
|
BI 编译器 C语言
初识C语言一
初识C语言一
65 0
|
C语言
C语言的部分杂碎知识
C语言的部分杂碎知识
|
存储 编译器 C语言
初识C语言(2)
在前面我们讲解C语言的发展历史,第一个C程序,数据类型,变量与常量,今天我们继续学习初识C语言后面的内容。
初识C语言(2)
|
C语言
【C语言】汉罗塔
【C语言】汉罗塔
102 0
【C语言】汉罗塔
|
存储 人工智能 C语言
C语言假期作业 DAY 09
C语言假期作业 DAY 09
|
存储 Java 编译器
初识C语言(一)
初识C语言(一)