在C语言编程中使用变量的基础教程_C 语言

C语言在明面上将数的变量分为两类,整型变量以及浮点数,对应着现实世界的整数和小数。

首先是整数,使用了这么多的C语言之后,每当在使用整数之时都会将其想象成二进制的存在,而不是十进制。原因在于,这是程序的本质所在,稍有研究编译器工作原理的都会发现,在编译器处理乘法乃至除法的时候,优秀的编译器总会想方设法的加快程序的速度,毫无疑问在所有运算中移位运算是最快速的"乘法"以及"除法":

1<<2 == 4 ,8>>2 == 2

而正常一个乘法相当于十数次的加法运算的时间消耗,移位则不用(除法的消耗更大,但是随着CPU的进步,这些差距正在逐渐缩小,就目前来看依旧是有着不小的差距但无论如何优化,乘法时间都会大于加法)。正如前面所说,C语言设计之初便是给了程序员所有的权利,而程序员要做的就是掌控所有能掌控的,即便是数的计算亦是如此,比如在优秀的编译器看来:

2*7 ====> (2<<3) - 2
5*31 ====> (5<<5) - 5

毫无疑问经过编译器优化后的代码此前者要快许多。这就是为什么我们要将一个数看作二进制,这不仅仅是表面,而是要在深层次的认为它是二进制,总体来说C语言的整型是非常简洁明了的总体分为 有符号 和 无符号,很好理解只需要注意不要让无符号数进行负数的运算,这里有一个原则,可以很好的规避这种无意之过,不把无符号类型变量和有符号类型变量放于同一运算中,时刻记得保持式子的类型一致是设计时的保障。

浮点数,由于实数域可以看作稠密的,故除了整数以外,还有无数的小数,而小数在计算机中如何表示?一种无限的状态是无法在计算机中被精确表示,所以有了浮点法,关于浮点法可以参考书籍《深入理解计算机系统》。
这里介绍的是在C语言中我们应该如何正确使用浮点数?很多人(包括我)在初作之时总是想当然的以为计算机是无所不能的,连人类都无法完全表达出来的小数计算机一定可以,实际上并非如此,在这里我可以说,计算机只是近似表达,而最大的忌讳的便是将两个浮点数进行比较,此处介绍一种浮点数常用的比较方法,精确度法:

 #define DISTANCE 0.00000001
 ...
 float f_x_1 = 20.5;
 float f_x_2 = 19.5;
 if(f_x_1 - f_x_2 < DISTANCE)
   printf("They are Equal\n");
 else
   printf("Different\n");

所以说,在很大程度上,当你在程序中使用了浮点数,又直接使用浮点数进行比较,却发现始终无法达到预期效果,那么你可以检查一下,是否是这个原因,在这一点上,不得不说是C语言的一个缺憾。

指针变量,是一种比较特别的变量,以至于总是对它进行特别对待。这里有几个原则:

  • 两个不相关的指针进行加减操作是无意义的
  • 始终确保自己能够找到分配的内存
  • 无论何时何地何种情况,都要记住,不使用未初始化的指针,不让未使用的内存持续存在。

指针在不同位的操作系统上的大小是不一样的,但是在同一个操作系统下,无论什么类型的指针都是相同大小,这涉及到指针的寻址问题,(题外话:C语言的寻址实际上使用了汇编语言的间接寻址,有兴趣的可以自行尝试,方法之一,使用gcc编译器的汇编选项,产生汇编代码,进行一一比对),对于寻址一个笼统一些的说法便是

4Byte = 32bit
2^32 = 4G

所以32位的操作系统下C语言指针:

 ...
 size_t what = sizeof(void*);
 printf("%d", what);
 ...

输出:

$root@mine: 4

对于大部分使用者来说,指针主要用来降低内存消耗以及提高运算效率的,这里设计许多学问,我也无法一一展示,比较有意思也常用的两个东西便是递增以及语法糖:++, ->

 ...
 int dupli_of_me[10] = {0};//也可以使用库函数memset()进行置0
 int *point_to_me = dupli_of_me;
 int me = 100;
 while(point_to_me < (dupli_of_me + 10))
   *point_to_me++ = me;

其中*point_to_me++ = me;在C语言应用广泛它相当于是:

 *point_to_me = me;
 point_to_me++;

的语法糖,对于++,在非必要的情况下,请使用前缀递增,而非后缀递增,原因是消耗问题,仔细想想这两种递增的区别在何处?
前缀递增总是在原数上进行递增操作,然而后缀递增呢?它首先拷贝一份原数放于别处,并且递增这份拷贝,在原数进行的操作完毕后,将这份拷贝再拷贝进原数取代它,此中的操作涉及的更多,所以在非必要的情况下,请使用前缀递增而不是后缀递增(递减也是同样的道理)。
->则是在结构体上使用的非常广泛:

 typedef struct data{
   int test;
   struct data* next;
 }my_struct;
 ...
 my_struct temp;
 my_struct *ptemp = &temp;
 ptemp->test = 100;
 ptemp->next = NULL;
 if(temp.test == 100)
   printf("Correctly!\n");
 else
   printf("That is impossible!\n");
 ...

可以很清楚的看出其实ptemp->test便是(*ptemp).test的语法糖

变量限定

const 是最常用的变量限定符,它的意思是告诉编译器,这个变量或者对象在初始化以后不能被改变,常用它来保护一些必要的返回值,参数以及常量的定义。

volatile 这个关键字常常被C语言教材所忽略,它很神秘。实际上确实如此,他的作用的确很神秘:一旦使用了,就是告诉编译器,即使这个变量没有被使用或修改其他内存单元,它的值也可能发生变化。通俗的说就是,告诉编译器,不要把你的那一套优化策略用在我身上。

   /* 此时我们将编译器优化等级提高到 -O2 */
   int     test_num  = 100; //测试一个迭代加法
   int     nor_result = 0;
   volatile int vol_result = 0;
   /* 测试无volatile限定下,该程序的耗时 */
   for(int i = 0;i < 10000;++i)
     for(int j = 0;j < 10000;++j)
         nor_result += test_num;

接下来就是测试volatile限定下的代码

   for(int i = 0;i < 10000;++i)
     for(int j = 0;j < 10000;++j)
         vol_result += test_num;

在使用一些手段后,得到运行时间,可以很清晰的看出差别,在我的机器上,i5-4CPU,得到的结果是后者比前者慢大概十五倍。 从某一些方向上证明了,volatile的一些作用,比如调试的时候,或者一些特殊用途。涉足不多,故不记录。

变量说明

extern 用于将不同文件的,带有外部链接性的变量引用到本文件中。所谓外部链接性就是可以被除本文件外的其他文件"看见"的变量,如全局变量,使用方法:

 /* 以下为一个工程内可见 */
 /*file1.c*/
 int glo_show;//对于该全局变量来说,它们在声明时无初始化,则默认初始为0
 int glo_print = 10;//声明定义完成后,自动分配内存以存储信息
 ...

 /* file2.c */
 extern glo_print; //仅仅是引用名字,并不会额外分配空间
          //所以,只需要写正确变量名字即可,后方的初始化无须完全
          //因为变量的初始化定义只能有一次。

 void print()
 {
   printf("The Globle Value is %d \n", glo_print);
 }

auto 可以姑且忽略,因为没有什么实际意义。

变量获取

格式化输入输出在C语言的初学中使用的比较频繁,但是到后期会发现,由于I/O操作过于消耗资源,换句话来说就是会极大影响程序的执行效率,会渐渐的在发行版程序中消除。

常见格式化输入标准函数: sacnf, fscanf, sscanf

对于常见的使用不赘述,有两种比较不常见的格式:`%[]` 和 `%*`,
前者是用于限制读取类型,常见于字符串的过滤(不是真正的过滤)

    scanf("%d %[a-z]", &tmp, str);
    scanf("%d %[^i]", &tmp, str);
    scanf("%d %[^,]", &tmp, str);

假设输入的是:22 hello,string to me!
读取到的分别为:22 hello 和 22 hello,str 和 22 hello
后者则是忽略第一个输入:

    scanf("%*d %d", &tmp);

假设输入的是:22 33
读取到的则是:33
其中开头的%*d忽略的输入,必须和其类型匹配,例如输入:string 33则会读取失败。
也可以将其解读为文件宽度,例如在使用printf格式化输出的时候:

  char str[10] = "dir";
  printf("%*s%s",4 ,"" , str);
  /* 输出:  dir */ 四个空白占位

但是实际上scanf并不太好用,所谓的好用指的是功能上以及设计上的缺陷,总是让很多人摸不着头脑的出了错,往往很难调试。例如它会将每一行输入的\n保留在输入流里面,这个缺陷导致如果不明所以得人将其与其他的输入函数,例如fgets或者gets配合会出现差错。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c语言
变量
c语言编程基础、c语言基础编程题、c语言编程基础知识、c语言基础编程练习题、c语言基础与编程实践,以便于您获取更多的相关知识。

时间: 2025-01-02 14:35:31

在C语言编程中使用变量的基础教程_C 语言的相关文章

C语言编程中函数的基本学习教程_C 语言

C 语言中的函数等价于 Fortran 语言中的子程序或函数,也等价于 Pascal 语言中的过程或函数.函数为计算的封装提供了一种简便的方法,此后使用函数时不需要考虑它是如何实现的.使用设计正确的函数,程序员无需考虑功能是如何实现的,而只需知道它具有哪些功能就够了.在 C 语言中可以简单.方便.高效地使用函数.我们经常会看到在定义后仅调用了一次的短函数,这样做可以使代码段更清晰易读. 到目前为止,我们所使用的函数(如 printf.getchar 和 putchar 等)都是函数库中提供的函数

C语言编程中生成随机数的入门教程_C 语言

语言产生随机数是一个常见的编程功能任务,当然这个也不难,调用两三个函数就出来了,但是你知道这些函数具体是起到怎样的作用,并且是它们是如何产生随机数的吗? 几个概念 随机数:数学上产生的都是伪随机数,真正的随机数使用物理方法产生的. 随机数种子:随机数的产生是由算术规则产生的,srand(seed)的随机数种子不同,rand()的随机数值就不同,倘若每次的随机数种子一样,则rand()的值就一样.所以要产生随机数,则srand(seed)的随机数种子必须也要随机的. 用srand()产生随机数种子

讲解C语言编程中指针赋值的入门实例_C 语言

从const int i 说起 你知道我们声明一个变量时象这样int i :这个i是可能在它处重新变赋值的.如下: int i = 0; /* . . . */ i = 20; /*这里重新赋值了*/ 不过有一天我的程序可能需要这样一个变量(暂且称它变量),在声明时就赋一个初始值.之后我的程序在其它任何处都不会再去重新对它赋值.那我又应该怎么办呢?用const . /* . . . */ const int ic =20; /* . . . */ ic = 40; /*这样是不可以的,编译时是无

C语言编程中分配内存空间的相关函数_C 语言

C语言malloc()函数:动态分配内存空间头文件: #include <stdlib.h> malloc() 函数用来动态地分配内存空间(如果你不了解动态内存分配,请查看:C语言动态内存分配及变量存储类别),其原型为: void* malloc (size_t size); [参数说明]size 为需要分配的内存空间的大小,以字节(Byte)计. [函数说明]malloc() 在堆区分配一块指定大小的内存空间,用来存放数据.这块内存空间在函数执行完成后不会被初始化,它们的值是未知的.如果希望

详解C++编程中对于函数的基本使用_C 语言

形式参数和实际参数 在调用函数时,大多数情况下,函数是带参数的.主调函数和被调用函数之间有数据传递关系.前面已提到:在定义函数时函数名后面括号中的变量名称为形式参数(formal parameter,简称形参),在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为实际参数(actual parameter,简称实参). [例]调用函数时的数据传递. #include <iostream> using namespace std; int max(int x,int y)

基础C语言编程时易犯错误有哪些_C 语言

C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下"灵活的余地",但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误.看着有错的程序,不知该如何改起,通过对C的学习,积累了一些C编程时常犯的错误,以供参考. 1.书写标识符时,忽略了大小写字母的区别. main() { int a=5; printf("%d",A); } 编译程序把a和A认为是两个不同的变量名,而显示出错信息.C认为大写

C++编程中的函数指针初步解析_C 语言

1.定义每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针.2.语法指向函数的指针变量的一般定义形式为: 数据类型 (*指针变量名)(参数表);3.说明1) 函数指针的定义形式中的数据类型是指函数的返回值的类型. 2) 区分下面两个语句: int (*p)(int a, int b); //p是一个指向函数的指针变量,所指函数的返回值类型为整型 int *p(int a, int b); //p是函数名,此函数的返回值类型为整型指针 3) 指向函数的指针变量不是

详解C++设计模式编程中对状态模式的运用_C 语言

状态模式:当一个对象的内在状态发生变化时,允许改变其行为,这个对象看来像是改变了其类. 状态模式与策略模式的UML图几乎一模一样,下面列举了两者的不同: (1)可以通过环境类状态的个数来决定是使用策略模式还是状态模式. (2)策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类:而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系. (3)使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时

C++设计模式编程中的迭代器模式应用解析_C 语言

迭代器模式:提供一种方法顺序访问一个聚合对象中个各个元素,而不暴露该对像的内部表示. 迭代器模式应该是最为熟悉的模式了,最简单的证明就是我在实现组合模式.享元模式.观察者模式中就直接用到了 STL 提供的迭代器来遍历 Vector 或者 List数据结构. 迭代器模式也正是用来解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个类中进行,这样就避免了暴露这个聚合对象的内部表示的可能. 模式的动机: (1)一个聚合对象,如一个列表(List)或者一个集合(Set),应该提供一种方法来让别人可以访