变量的存数类型:
1:静态变量:凡是在代码任何快之外声明的变量总是存储在静态内存内,也就是不属于堆栈的内存。
对于这类变量。你无法对它们制指定存储类型。
2:存储于堆栈中,称为自动变量。当程序执行到声明自动变量的代码时,自动变量才被创建。
当程序的执行流离开该代码时,这些自动变量将被自行销毁。
3:寄存器变量:关键字register可以用于自动变量的声明,提示它们应该存储于机器的硬件寄存器,而不是
内存中,这类变量称为寄存器变量。
通过一个指针访问它所指向的地址的过程称为间接访问或者解引用指针。这个用于执行间接的操作符是单目操作符*。
如:d的值是100,当我们对D使用间接访问操作符时,它表示访问内存地址100,并查看那里的值。因此:*d的市里面存储
的内容,
d是一个指向整型的指针,对它进行引用操作符将产生一个整型值。类似,对float *进行间接访问将产生一个float型的值。
关于野指针和空指针:
下面的代码是很明显的错误:
int *a;
...
*a=12;
这个声明创建了一个名叫a的指针变量,后面那条赋值语句把12存储在a所指向的内存位置。
警告:
但是究竟a指向哪里呢?我们声明了这个变量,但是从来未对其进行初始化,所以我们没有办法预测12这个直讲存储于什么地方。
从这一点看,指针变量和其它变量并没有什么区别。如果变量是静态的。他会被初始化为0.但是如果变量是自动的,它根本不会被初始化。
空指针:
#include"stdio.h"
int main()
{
printf("%d\n",NULL);//0
}
标准定义了NULL指针,它作为一个特殊的指针变量,表示不指向任何东西。要是一个指针变量为NULL,你可以给它赋一个零值。为了测试
一个指针变量是否为NULL,你可以将它与零值进行比较。之所以选择零这个值是因为一种源代码约定。就机器内部而言,NULL指针的实际值可能
与此不同。在这种情况下,编译器将负责零值与内部值之间的转换。
NULL指针的概念是非常有用的,因为它给了你一种用法,表示某个特定的指针目前并为之向任何东西。比如:一个用于在某个数组中查找特定值的函数,
可能返回一个指向查找到的数组元素的指针。如果该数组不包含指定条件的值,函数就返回一个NULL指针,这个技巧允许返回值传达两个不同片段的信息。
首先,有没有找到元素?其次,如果找到,它是哪个元素呢?
尽管这个技巧在C程序中,极为常用。但是他违背了软件工程的原则,用一个单一的值表示两种不同的意思是件很危险的事情,因为将来很容易无法
弄清哪个才是它真正的用意。在大型的程序中,这个问题更加严重,因为你不可能在头脑中对整个设计一览无余,一种更为方便的策略是让函数返回两个独立的值。:
首先是个状态值,用于提示查找是否成功;其次是个指针,当状态值提示查找成功时,它所指向的就是要查找的元素。
对指针进行解引用操作可以获得它所指向的值。但是从定义上来看,NULL指针并为之向任何东西。因此,对于一个NULL指针进行解引用操作是非法的。
在对指针进行解引用之前,你必须确定它非NULL指针。
警告:如果对一个NULL指针进行间接访问会发生什么情况呢?这个视编译器而定。在有些机器上,它会访问内存位置零,便以其能够确保内存位置零
没有存储任何变量,但机器并未阻碍你访问这个位置,这种行为是非常不幸的,因为程序包含了一个错误,但是机器却隐匿了它的症状,这样就使得这个错误难以查找。
在其他机器上,对NULL指针进行间接访问将引发一个错误,并终止程序。宣布这个错误比隐藏这个错误要好得多,因为程序员更容易修正它。
提示:
如 果所有的指针变量(而不仅仅是位于静态中的指针变量)能够被初始化为NULL,那确实是件幸事,但事实并非如此。不论你的机器对解引用NULL指针这种行 为作何反应,对所有的指针变量进行显示的初始化就是最好的做法。如果你已经知道指针将被初始化为什么类型的地址,就把它初始化为该地址,否则就把他初始化 为NULL。风格良好的程序会在解引用之前对它进行检查。这种初始化策略可以节省大量的调试时间。
指针:
*&a=25;
这句代码的含义是:把值25赋值给变量a。
首先&操作符产生变量a的地址,它一个指针常量(注意:使用这个指针并不需要知道它的实际值),接着,*操作符访问其操作数所标示的地址。在这个表达式中,操作数是a的地址,所以值25就直接存储于a中。
另一大难点:
假定变量a存储于位置100,那么下面这条语句的作用是:
* 100=25;
它上去像是把25的值赋值给a,因为a是位置100所存储的变量。但是,这是错的。
这条语句上是非法的。因为字面值100的类型是整型,而间接访问操作只能作用于指针类型表达式。如果你确实想把25存储于位置100,你必须使用强制类型转换。
*(int *)100=25;
强制类型转换把值100从“整型”转换为”整型的指针”,,这样对它进行间接访问,就是合法的。如果a存储于位置100,那么这个条语句就是把值25存储于a。
//计算一个字符串的长度
#include"stdio.h"
#include"stdlib.h"
strlen(char *string)
{
int length=0;
/*
*依次访问字符串的内容,计数字符数,直到遇见NULL终止符。
*/
while(*string++!=‘\0‘)
{
length+=1;
}
return length;
}
2:关于字符串的输入和输出:
C语言中,没有字符串类型,用字符数组处理字符串。
字符数组定义:
char 数组名[常量表达式] [,[常量表达式]];
说明:一维字符数组,用于存储和处理一个字符串,二维字符数组,用于同时存储和处理多个字符串;
因为字符型与整型是通用的,可以用int来定义字符数组,但两者有区别,如:
char c[10]; /* 在内存中占10字节 */
int c[10]; /* 在内存中占40字节 */
输入输出方法:逐个字符输入输出:%c,整个字符串输入输出:%s
一:用scanf()输入字符串,printf()输出字符串:
逐个字符处理:
#include "stdio.h "
void main()
{ char ch[5];
int i;
for(i=0;i<5;i++)
scanf(“%c”, &ch[i]);
for(i=0;i<5;i++)
printf(“%c”, ch[i]);
}
整个字符串处理:
#include "stdio.h "
void main()
{ char ch[5];
scanf(“%s”,ch);
printf(“%s”, ch);
}
说明:
以字符串为单位处理时,直接使用数组名,无需&;
输入字符串时,字符个数要小于数组的长度,如输入5个字符,定义的字符数组至少应有6个元素;
输入字符串时,若遇空格或回车,输入结束,并自动在串后加上结束标志’\0’;
输出字符串时,遇到字符串结束标志’\0’,输出结束。
二:用字符串处理函数输入和输出
字符串标准函数的原型在头文件string.h中(使用字符串处理函数输入和输出时要在头文件上加入 #include<string.h>)
字符串输出函数puts
格式:puts(字符数组)
功能:向显示器输出字符串(输出完,自动换行,即用’\n’替换了’\0’)
说明:字符数组必须以‘\0’结束
字符串输入函数gets
格式:gets(字符数组)
功能:从键盘输入一以回车结束的字符串放入字符数组中,并自动加‘\0’
说明:输入串长度应小于字符数组维数,字符串中可以包含空格
例如:
#include<stdio.h>
#include<string.h>
void main()
{
char str[10];
int i;
printf("请输入字符串:\n");
gets(str);
printf("输入的字符串为:\n");
puts(str);
}
数组:
定义数组的大小必须使用整型常量或者整型常量表达式。C语言中不允许用变量下表形式对数组进行动态定义。例如:下面的数组定义语句:
Short score[100]; //正确的定义形式
而:
Short score[N]; //不正确的定义方式
是非法的,即使在数组定义的语句前面使用赋值语句给N进行了赋值,或者使用scanf()输入了N值,上面这条语句也是非法的。
关于使用scanf输入数据或许你还有好多疑问,下面是一个师哥(zy)对我的疑惑的讲解。
Zy:字符串的本质就是个char*。scanf输入字符串,就是往指定的位置,其实就是
I:但是ch[5]不是定义了一个存放五个字符的数组么,我输入的超过五个且少于十个程序就没有停止。
下面给你一个案例:
int a=99,b=99;
float c=9999,d=99.99;
scanf("%d %d %f %f",&a,&b,&c,&d);
Printf("%d %d %f %f\n",a,b,c,d);
当输入内容是:1.23 4.5 6.7,知道这四个变量值怎么变化吗?
由于实在不确定它的结果,在VC环境下调试下:
当我从键盘上依次输入:1.23 4.5 6.7(回车),
输出结果却是:1 99 9999.000000 99.989998
对于小数点后的输出结果:浮点型数据,小数点后有六位有效数字,它随操作系统而变化,不是确切的数,不是直接补零。
但是为什么第三个数能够进行确切的输出,小数点后面全是0,而第四个数为什么小数点后面的数是那些呢?
因为这个你输入的是9999,本身小数点后面没有输入,所以自然补零
由于float是系统默认为6位有效数字,float是单精度型
double是双精度型的,最高可以到16位。
由计算结果可以看出:
就a被改写了。后面类型不匹配,scanf被自动终止。
但是,现在的问题是,明显就那个1被读走了,敲的后面那段内容哪去了?
或者这样,这个56去哪了?
为什么当从键盘上依次输入 12(空格)34(空格)56(空格)之后,为什么程序不等让用户输入C,就直接输出执行结果?
刚刚这个,明显是一开始要两个数,敲了三个,多一个
而第二次要求输入c的时候,程序就没停,没让输入,直接过去了
但是,这是什么原因呢:
因
为scanf语句你只让输入两个数据a和b,但是你在键盘上输入的时候却直接输入了三个整数,实际上scanf(“%d”,&c);已经执行了,
输入:12 34 56就相当于直接把56传给C,这样的执行等同于将两行scanf语句一同执行,但是在编写程序的过程中,这种输入法是不
提倡的。
补充:
对
于scanf(“%d%d”,&a,&b);中的双引号中的格式符,如果在两个%d中间加上一个空格,那么在键盘上进行输入时,一定要严
格遵从scanf中的形式,如:12(空格)34,但是如果将scanf()这条语句修改为:scanf(“%d%d”,&a,&b);
那么在键盘上输入的时候在两个int类型的数字之间,你可以任意输入空格,回车,或者Tab毽作为两个数据之间的分隔符。所以提倡使用scanf语句时,
在双引号之间只有格式符。双引号里直接写代码,别写空格。
,scanf输入的所有东西,都是先写到内存里,然后往变量里放。虽然这三个auto型变量也是在内存中,这么说很奇怪。
scanf对应的那里叫做stdin标准输入流,你敲击的东西都被顺序放入了标准输入流里面。
scanf("%d%d",&a,&b);
先把你敲击的东西写入stdin,然后再从stdin里顺次把数据转换成按照指定的类型(第一个是%d),也就是输入的字符流12转换成int型的12,写入指定的位置(第一个%d对应的是&a)
输入流内容是12 34 56
输入格式指定顺次为%d%d,也就是说一上来就要整数
基于贪心法,1是整数,合法;12是整数,合法;12空格不合法
那么%d对应的是12,把字符流12转换成int型的12,写到变量a中