- 相关推荐
C语言字符型基础知识
在学习中,大家都背过各种知识点吧?知识点在教育实践中,是指对某一个知识的泛称。相信很多人都在为知识点发愁,下面是小编为大家整理的C语言字符型基础知识,仅供参考,希望能够帮助到大家。
C语言字符型基础知识
1. 字符型数据
文字处理是计算机的一个重要应用领域,这个应用领域的程序必须能够使用和处理字符形式的数据。在C语言中,字符型数据包括字符和字符串两种,例如'a'是字符,而"Windows" 是字符串。
字符型数据在计算机中存储的是字符的ASCII码(ASCII码表见附录A),一个字符的存储占用一个字节。因为ASCII码形式上就是0 到255之间的整数,因此C语言中字符型数据和整型数据可以通用。例如,字符"A"的ASCII码值用二进制数表示是1000001,用十进制数表示是65,在计算机中的存储示意图见图3-6。由图可见,字符"A"的存储形式实际上就是一个整型数65,所以它可以直接与整型数据进行算术运算、混合运算,可以与整型变量相互赋值,也可以将字符型数据以字符或整数两种形式输出。以字符形式输出时,先将ASCII码值转换为相应的字符,然后再输出;以整数形式输出时,直接将ASCII码值作为整数输出。
2.字符型常量
字符常量亦被称为字符常数。C语言中字符常量是括在一对单引号内的一个字符。 例如:'x'、'B'、'b'、'$'、'?'、' '(表示空格字符)、'3'都是字符常量,注意其中'B'和'b'是不同的字符常量。
除了以上形式的字符常量外,对于常用的但却难以用一般形式表示的不可显示字符,C语言提供了一种特殊的字符常量,即用一个转义标识符""开头,后续需要的转义字符来表示。常用的转义字符序列的字符常量见表3-4。
转义字符是一种特殊形式的字符常量,其意思是将转义符""后的字符原来的含义进行转换,变成某种另外特殊约定的含义。
例如,转义字符" "中的n已不代表字符常量"n",由于n前面是转义符"",所以n就转义成换行。转义字符"15"是"ddd"形式的转义字符,其中"015"是八进制字符串,它表示了ASCII码表中编码为十进制13的字符,也就是回车。转义字符"x1f"是"xdd"形式的转义字符,其中"1f"是十六进制字符串,它表示了ASCII码表中编码为十进制31的字符,也就是▼。
可见,用转义字符方法可以表示任何可显示或不可显示的字符。在实际应用中,转义字符的使用很多,例如:例3-2中有以下程序行:
printf("a=%f,b=%f ",a,b);
其中的" "就是转义字符换行。几乎每个程序中都会有一个或若干个这样的程序行。要注意其使用。
3. 字符型变量
字符型变量用于存放字符常量,即一个字符型变量可存放一个字符,所以一个字符型变量占用1个字节的内存容量。说明字符型变量的关键字是char,使用时只需在说明语句中指明字符型数据类型和相应的变量名即可。例如:
char s1, s2; /* 说明 s1,s2 为字符型变量 */
s1='A'; /* 为s1赋字符常量'A' */
s2='a'; /*为s2赋字符常量'a' */
4. 字符串常量
字符串常量是用一对双引号括起来的字符序列。这里的双引号仅起到字符串常量的边界符的作用,它并不是字符串常量的一部分。例如下面的字符串都是合法的字符串常量:
"I am a student. ","ABC"," ","a"
注意不要把字符串常量和字符常量混淆,如"a"和'a'是根本不同的数据,前者是字符串常量,后者是字符常量。如果字符串常数中出现双引号,则要用反斜线'"'将其转义,取消原有边界符的功能,使之仅作为双引号字符起作用。例如,要输出字符串:
He says:"How do you do."
应写成如下形式:
printf ("He says:"How do you do."");
C语言对字符串常量的长度不加限制,编译程序总是自动地在字符串的结尾加上一个转义字符''(即ASCII码是0,所对应的字符是空),作为字符串常量的结束标志。对字符串操作时,这个结束标志是非常重要的。例如输出字符串时,遇到这个结束标志才终止输出。
可见,字符常量与字符串常量的区别有两个方面:从形式上看,字符常量是用单引号括起的单个字符,而字符串常量是用双引号括起的一串字符;从存储方式看,字符常量在内存中占一个字节,而字符串常量除了每个字符各占一个字节外,其字符串结束符''也要占一个字节。例如:字符常量'a'占一个字节,而字符串常量"a" 占2个字节,如图3-7示意图所示。
C语言没有专门的字符串变量,如果需要处理字符串,一般用字符型数组来实现。关于字符数组及其它字符数据处理问题在本书第八章作详细介绍。
5. 字符数据的应用举例
例3-3:计算字符'A'与整型数据25的和。
/* L3_3.C */
Main()
{ char a; /* 说明a为字符型变量 */
int b; /* 说明b为整型变量 */
a='A'; /* 为a赋字符常量'A' */
b=a+25; /* 计算65+25并赋值给字符变量b */
printf("%c,%d,%c,%d ",a,a,b,b); /* 分别以字符型和整型两种格式输出a、b */
}
程序运行的输出结果如下:
A,65,Z,90
上述程序中a变量的值是'A',实际存放的是'A'的ASCII码65,它可直接与十进制整型常量25相加,所得整型数据90赋值给变量b,而90是大写字符'Z'的ASCII码,所以可以将a、b变量分别以字符型和整型两种格式输出。可见字符型数据和整型数据是可以通用的。
C语言的最大特点是:功能强、使用方便灵活。
C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。
1.书写标识符时,忽略了大小写字母的区别。
2.忽略了变量的类型,进行了不合法的运算。
3.将字符常量与字符串常量混淆。
4.忽略了“=”与“==”的区别。
5.忘记加分号。分号是C语句中不可缺少的一部分,语句末尾必须有分号。
6.多加分号。 复合语句的花括号后不应再加分号,否则将会画蛇添足。
7.输入变量时忘记加地址运算符“&”。
8.输入数据的方式与要求不符。代码①scanf("%d%d",&a,&b);输入时,不能用逗号作两个数据间的分隔符②scanf("%d,%d",&a,&b);C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。
9.输入字符的格式与要求不一致。在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。
10.输入输出的数据类型与所用格式说明符不一致。
11.输入数据时,企图规定精度。
12.switch语句中漏写break语句。
13.忽视了while和do-while语句在细节上的区别。
14.定义数组时误用变量。
15.在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。
16.初始化数组时,未使用静态存储。
17.在不应加地址运算符&的位置加了地址运算符。
18.同时定义了形参和函数中的局部变量。
运算符
分为以下几类:
1、算术运算符:用于各类数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余(%)、自增(++)、自减(--)共七种。
2、赋值运算符:用于赋值运算,分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=)三类共十一种。
3、逗号运算符:用于把若干表达式组合成一个表达式(,)。
4、关系运算符:用于比较运算。包括大于(>)、小于(<)、等于(==)、>=)、小于等于(<=)和不等于(!=)六种。
5、逻辑运算符:用于逻辑运算。包括与(&&)、或(||)、非(!)三种。
6、条件运算符:这是一个三目运算符,用于条件求值(?:)。
7、位操作运算符:参与运算的量,按二进制位进行运算。包括位与(&)、位或(|)、位非(~)、位异或(^)、左移(<<)、右移(>>)六种。
8、指针运算符:用于取内容(*)和取地址(&)二种运算。
9、求字节数运算符:用于计算数据类型所占的字节数(sizeof)。
10、特殊运算符:有括号(),下标[],成员(→,.)等几种。
另外,按参与运算的对象个数,C语言运算符可分为:单目运算符 (如 !)、双目运算符 (如+,- )和三目运算符 (如 ? : )。
算术运算符和算术表达式
一、基本的算术运算符
(1)+(加法运算符或正值运算符,如2+5)。
(2)-(减法运算符或负值运算符,如4-2)。
(3)*(乘法运算符,如3*8)。
(4)/(除法运算符,如11/5)。
/的运算分为两种情况:
a、“除”的左右两边都为整数时,所得结果必然是整数(注意:仅取整数部分,不是四舍五入)
比如:5/2的值为2,不是2.5,1/2的值为0。
b、“除”的左右两边至少有一个是实型数据(即小数)时,所得结果为实型数据。
比如:5/2.0的值为2.5,7.0/2.0的值为3.5.
(5)%(模运算符或称求余运算符,%两侧均应为整型数据,如9%7的值为2)。
需要说明的是:当运算对象为负数时,所得结果随编译器不同而不同,在vc中,结果的符号与被除数相同,比如:13%-2值为1,而-15%2值为-1。
二、 算术表达式和运算符的优先级与结合性
算术表达式是用算术运算符和括号将运算量(也称操作数)连接起来的、符合C语言语法规则的表达式。运算对象包括函数、常量和变量等。
在计算机语言中,算术表达式的求值规律与数学中的四则运算的规律类似,其运算规则和要求如下。
(1)在算术表达式中,可使用多层圆括号,但括号必须配对。运算时从内层圆括号开始,由内向外依次计算各表达式的值。
(2)在算术表达式中,对于不同优先级的运算符,可按运算符的优先级由高到低进行运算,若表达式中运算符的优先级相同,则按运算符的结合方向进行运算。
(3)如果一个运算符两侧的操作数类型不同,则先利用自动转换或强制类型转换,使两者具有相同类型,然后进行运算。
三、 自增自减运算符
作用:使变量的值增1或减1。
如:++i,--i (在使用i之前,先使i的值加1、减1)。
i++,i-- (在使用i之后,使i的值加1、减1)。
(1)只有变量才能用自增运算符 (++)和自减运算符(--),而常量或表达式不能用,如10++或(x+y)++都是不合法的。
(2)++和--的结合方向是“自右向左“,如 -i++ ,i的左边是负号运算符,右边是自增运算符,负号运算和自增运算都是 “自右向左“结合的,相当于 -(i++)。
在循环语句中常用到自增(减)运算符,在指针中也常用到该运算符,考生要弄清楚“i++”和“++i”及“i--”和“--i”的区别,特别弄清楚表达式的值和变量的值。
一、位运算符
在计算机中,数据都是以二进制数形式存放的,位运算就是指对存储单元中二进制位的运算。C语言提供6种位运算符。
二、位运算
位运算符 & |~<< >> ∧ 按优先级从高到低排列的顺序是:
位运算符中求反运算“~“优先级最高,而左移和右移相同,居于第二,接下来的顺序是按位与 “&“、按位异或 “∧“和按位或 “|“。顺序为~ << >> & ∧ | 。
例1:左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。
例如:
a<<4
指把a的各二进位向左移动4位。如a=00000011(十进制3),左移4位后为00110000(十进制48)。
例2:右移运算符“>>”是双目运算符。其功能是把“>> ”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。
例如:
设 a=15,
a>>2
表示把000001111右移为00000011(十进制3)。
应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补0,而为负数时,符号位为1,最高位是补0或是补1 取决于编译系统的规定。
例3:设二进制数a是00101101 ,若通过异或运算a∧b 使a的高4位取反,低4位不变,则二进制数b是。
解析:异或运算常用来使特定位翻转,只要使需翻转的位与1进行异或操作就可以了,因为原数中值为1的位与1进行异或运算得0 ,原数中值为0的位与1进行异或运算结果得1。而与0进行异或的位将保持原值。异或运算还可用来交换两个值,不用临时变量。
如 int a=3 , b=4;,想将a与b的值互换,可用如下语句实现:
a=a∧b;
b=b∧a;
a=a∧b;
所以本题的答案为: 11110000 。
C语言中标识符的命名规则如下:
标识符只能由字母、数字、下划线组成;
标识符的第一个字母必须是字母和下划线;
标识符区分大小写字母,如If和if是两个完全不同的标识符。
合法标识符如下:
A6, b_3 , _mn
非法的标识符如下:
ab#12 , 8m , tr3:4 , yes no
标识符不能与程序中具有特殊意义的关键字相同,不能与用户编制的函数名、C语言库函数相同,在程序中各种标识符尽量不要重复,以便区分。选择变量名和其他标识符时,应注意做到 “见名知义”。
标识符分为如下三类:
1、关键字
关键字是具有特定含义的,专门用来说明c语言特定成分的一类标识符,不能用作用户的标识符。
auto
break
case
char
union
do
double
else
enum
extern
goto
if
int
long
short
signed
static
sizof
struct
switch
unsigned
void
for
while
typedef
continue
float
return
typedef
default
2、预定义标识符
预定义标识符在c语言中也有特定的含义,但可以用作用户标识符,预定义标识符分为两类:
(1)、库函数名字,比如(printf,scanf,sin,isdigit等)
(2)、编译处理命令名,比如(define,include)
3、用户标识符
用户根据需要自己定义的标识符称为用户标识符。无论如何自定义标识符,都必须符合标识符的三条命名规则。
C语言
char *strchr(const char* _Str,int _Val)
char *strchr(char* _Str,int _Ch)
头文件:#include
功能:查找字符串s中首次出现字符c的位置
说明:返回首次出现c的位置的指针,返回的地址是被查找字符串指针开始的第一个与Val相同字符的指针,如果s中不存在c则返回NULL。
返回值:成功则返回要查找字符第一次出现的位置,失败返回NULL
参数编辑
haystack
输入字符串。
needle
如果 needle 不是一个字符串,那么它将被转化为整型并且作为字符的序号来使用。
before_needle
若为 TRUE,strstr() 将返回 needle 在 haystack 中的位置之前的部分。
返回: 返回字符串的一部分或者 FALSE(如果未发现 needle)。
例子:
1
2
3
4
5
6
7
$email='name@example.com';
$domain=strchr($email,'@');
echo$domain;//打印@example.com
$user=strchr($email,'@',true);//从PHP5.3.0起
echo$user;//打印name
?>
函数公式编辑
实现:
1
2
3
4
5
6
7
8
char*strchr(char*s,charc)
{
while(*s!=''&&*s!=c)
{
++s;
}
return*s==c?s:NULL;
}
范例
举例1:(在Visual C++ 6.0中运行通过)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include
#include
int main(void)
{
char string[17];
char *ptr,c='r';
strcpy(string,"Thisisastring");
ptr=strchr(string,c);
if(ptr)
printf("Thecharacter%cisatposition:%s ",c,ptr);
else
printf("Thecharacterwasnotfound ");
return0;
}
运行结果:
The character r is at position: ring
请按任意键继续. . .
举例2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// strchr.c
#include
#include
int main()
{
char temp[32];
memset(temp,0,sizeof(temp));
strcpy(temp,"Golden Global View");
char *s = temp;
char *p,c='v';
p=strchr(s,c);
if(p)
printf("%s",p);
else
printf("Not Found!"); return 0;
}
运行结果:Not Found!Press any key to continue
举例3:
1
2
3
4
5
6
7
8
9
10
11
#include
#include
void main()
{
char answer[100],*p;
printf("Type something: ");
fgets(answer,sizeof answer,stdin);
if((p = strchr(answer,' ')) != NULL)
*p = '';//手动将 位置处的值变为0
printf("You typed "%s" ",answer);
}
fgets不会像gets那样自动地去掉结尾的 ,所以程序中手动将 位置处的值变为,代表输入的结束。
函数说明
语法
指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
新的大小可大可小(但是要注意,如果新的大小小于原内存大小,可能会导致数据丢失,慎用!)
头文件
#include
功能
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
返回值
如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
注意
当内存不再使用时,应使用free()函数将内存块释放。
相关函数
1
malloc、calloc、free、_alloca
应用举例
举例1
从这个例子可以看出realloc函数的功能。
运行环境:ubuntu 12.04 GCC 4.6.3
运行结果:
malloc 0x904f008
realloc 0x904f008
0 1 2 3 4 5 6 7 8 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include
#include
int main()
{
inti;
int*pn=(int*)malloc(5*sizeof(int));
printf("malloc%p ",pn);
for(i=0;i<5;i++)
pn[i]=i;
pn=(int*)realloc(pn,10*sizeof(int));
printf("realloc%p ",pn);
for(i=5;i<10;i++)
pn[i]=i;
for(i=0;i<10;i++)
printf("%3d",pn[i]);
free(pn);
return 0;
}
举例2
:(在TC2.0中运行通过)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//realloc.c
#include
#include
main()
{
char*p;
clrscr();//clearscreen
p=(char*)malloc(100);
if(p)
printf("MemoryAllocatedat:%x",p);
else
printf("NotEnoughMemory! ");
get);
p=(char*)realloc(p,256);
if(p)
printf("MemoryReallocatedat:%x",p);
else
printf("NotEnoughMemory! ");
free(p);
get);
return 0;
}
内存分配编辑
如果有足够空间用于扩大mem_address指向的内存块,则分配额外内存,并返回mem_address。
这里说的是“扩大”,我们知道,realloc是从堆上分配内存的,当扩大一块内存空间时, realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平。也就是说,如果原先的内存大小后面还有足够的空闲空间用来分配,加上原来的空间大小= newsize。那么就ok。得到的是一块连续的内存。
如果原先的内存大小后面没有足够的空闲空间用来分配,那么从堆中另外找一块newsize大小的内存。
并把原来大小内存空间中的内容复制到newsize中。返回新的mem_address指针。(数据被移动了)。
老块被放回堆上。
例如
1
2
3
4
5
6
7
8
9
#include
voidmain()
{
char*p,*q;
p=(char*)malloc(10);
q=p;
p=(char*)realloc(q,20);//A行,通过realloc扩大p的空间,并把新的地址赋值给p。
//…………………………
}
在这段程序中我们增加了指针q,用它记录了原来的内存地址p。这段程序可以编译通过,但在执行到A行时,如果原有内存后面没有足够空间将原有空间扩展成一个连续的新大小的话,realloc函数就会以第二种方式分配内存,此时数据发生了移动,那么所记录的原来的内存地址q所指向的内存空间实际上已经放回到堆上了!这样就会产生q指针的指针悬挂,即指针指向了一块没有分配给用户使用的内存,如果再用q指针进行操作就可能发生意想不到的问题。所以在应用realloc函数是应当格外注意这种情况。
返回情况
返回的是一个void类型的指针:调用成功。(这就要求在你需要的时候进行强制类型转换)
返回NULL:当需要扩展的大小(第二个参数)为0并且第一个参数不为NULL时。此时原内存变成“free(游离)”的了。
返回NULL:当没有足够的空间可供扩展的时候。此时,原内存空间的大小维持不变。
特殊情况
如果mem_address为NULL,则realloc()和malloc()类似。分配一个newsize的内存块,返回一个指向该内存块的指针。
如果newsize大小为0,那么释放mem_address指向的内存,并返回NULL。
如果没有足够可用的内存用来完成重新分配(扩大原来的内存块或者分配新的内存块),则返回NULL。而原来的内存块保持不变。
现存的数据然后就被拷贝至新的位置,而老块则放回到堆上.重要的信息就是数据可能被移动
#include
#include
int main(int argc, char* argv[])
{
char *p,*q;
p = (char *)malloc(10);
q = p;
p = (char *)realloc(p,10);
printf("p=0x%x/n",p);
printf("q=0x%x/n",q);
return 0;
}
输出结果:realloc后,内存地址不变
p=0x431a70
q=0x431a70
例2:
#include
#include
int main(int argc, char* argv[])
{
char *p,*q;
p = (char *)malloc(10);
q = p;
p = (char *)realloc(p,1000);
printf("p=0x%x/n",p);
printf("q=0x%x/n",q);
return 0;
}
输出结果:realloc后,内存地址发生了变化
p=0x351c0
q=0x431a70
例
1 #include
2 #include
3 #include
4
5 int main(int argc, char **argv){
6
7 char *p, *p2, *pnew;
8 int offset = 0;
9
10 p = (char *)malloc(10);
11 if(!p){
12 printf("malloc p error ");
13 }
14 strcpy(p, "Hello,");
15 p2 = strchr(p,',');
16 offset = p2-p+1;
17
18 pnew = (char *)realloc((void *)p, 20);
19
20 if(pnew){
21 p = pnew;
22 p2 = pnew + offset;
23 strcpy(p2," world");
24 }
25 printf("string is: %s ",p);
26 return 0;
27 }
执行结果:string is: Hello, world
使用总结
1. realloc失败的时候,返回NULL
2. realloc失败的时候,原来的内存不改变,不会释放也不会移动
3. 假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址
4. 如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露。
5. 传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的
6.传递给realloc的指针可以为空,等同于malloc。
1.什么是进制
进制是一种计数的方式,常用的有二进制、八进制、十进制、十六进制。任何数据在计算机内存中都是以二进制的形式存放的。
我对进制的个人理解,二进制数是以2为计算单元,满2进1位的数;八进制数是以8为计算单元,满8进1位的数。
对于任何一个数字,我们都可以用不同的进制来表示,比如,十进制数12,用二进制表示为1100,用八进制表示为14,用十六进制表示为0xC。
2.进制的转换规则
遵循满进制值进1位,个位数变为0的原理,下面我们以十进制数18为例,对1-18中每一个数值转换各种进制做一个详细说明
转二进制:
①小于2,无需进1位,1的二进制值是1
②为二进制值1后面一个数,由于1+1满2,需要进1位,个位数变为0,所以2的二进制值是10
③为二进制值10后面一个数,由于11的个位数1小于2,无需进1位,所以3的二进制值是11
④为二进制值11后面一个数,由于11的个位数1+1满2,需要进1位,而二进制值11的位数1+1又满2,所以位数加1,最终转换结果为100
转换思路:
二进制值11+1 ->10+(1+1)(个位等于2,进1位,个位数变为0) ->(1+1)+0(位数满2,进1位) -> 100
以此类推,最终十进制数18的二进制转换结果是10010
转八进制:
1-7小于8,无需进1位,1-7的八进制由1-7表示
8为八进制值7后面一个数,由于7+1满8,需要进1位,个位数变为0,所以8的八进制值是10
以此类推,最终十进制数18的八进制转换结果是22
转十六进制:
十六进制中,个位数1-15分别为1 2 3 4 5 6 7 8 9 a b c d e f (a=10....f=15)
16为十六进制值c后面1个数,由于c+1满16,需要进1位,个位数变为0,所以16的十六进制是10。
最终十进制数18的十六进制转换结果是12
详细结果如下图所示(C语言把数字前面加0x的数认为是十六进制数)
3.C语言中int类型进制的声明以及占位符
虽然以下3个变量的赋值方式不同,但实际赋值结果都是18
//二进制类型数字加0b
int number1 = 0b10010;
//八进制类型数字加0
int number2 = 022;
//十六进制类型数字加0x
int number3 = 0x12;
八进制占位符:%o
十六进制占位符:%x
4.内存存储数据细节
我们知道,int类型数据占据4个字节,1个字节是8bit。并且任何数据在计算机内存中都是以二进制的形式存放的,所以内存需要用32个0或1来描述1个int类型数据。
由于18的二进制数是10010,我们将一个int类型变量赋值18,本质上是将这个变量的内存地址对应的32个bit位修改为:
0000 0000 0000 0000 0000 0000 0001 0010(未满31位,后面的数字用0填充:为什么是31而不是32呢,后面会介绍)
假设我们定义两个变量
int number1 = 12; int number2 = 13;
计算机会根据内存地址以由大到小的顺序进行分配内存空间,具体如下图所示:
5.进制的转换公式
二进制转十进制
0b1100 ->0*2的0次方 + 0*2的1次方 + 1*2的2次方 + 1*2的3次方 = 12
十进制转二进制
67 ->64+2+1 ->2的6次方+ 2的1次方 + 2的0次方 = 0b1000011
6.进制的其他知识
①.n位二进制能保存的整数范围公式:2的n次方-1
例如,3位的二进制数最大值为111,对应的十进制数字为7;5位的二进制数最大值为11111,对应的十进制数字为(2*2*2*2*2)-1 = 31。
②.负数的二进制保存规则是最左边的数字是1。例如,0000 0000 0000 0000 0000 0000 0001 0010 表示正整数,1111 1111 1111 1111 1111 1111 1110 1101表示负数
由此,我们就能推测出,int类型能保存的最大整数是2的(32-1)次方-1 = 2147483647。为什么要用32-1,很简单,32个bit中,必须抽1个bit位用来描述这个数字是正数还是负数。
C语言函数
包含文件:string.h
函数名: strstr
函数原型:
1
extern char *strstr(char *str1, const char *str2);
语法:
1
* strstr(str1,str2)
str1: 被查找目标 string expression to search.
str2: 要查找对象 The string expression to find.
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
例子:
1
2
3
char str[]="1234xyz";
char *str1=strstr(str,"34");
cout << str1 << endl;
显示的是: 34xyz
函数实现
1.Copyright 1990 Software Development Systems, Inc.
1
2
3
4
5
6
7
8
9
10
11
12
char *strstr(const char *s1,const char *s2)
{
int len2;
if(!(len2=strlen(s2)))//此种情况下s2不能指向空,否则strlen无法测出长度,这条语句错误
return(char*)s1;
for(;*s1;++s1)
{
if(*s1==*s2 && strncmp(s1,s2,len2)==0)
return(char*)s1;
}
return NULL;
}
2.Copyright 1986 - 1999 IAR Systems. All rights reserved
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char *strstr(constchar*s1,constchar*s2)
{
int n;
if(*s2)
{
while(*s1)
{
for(n=0;*(s1+n)==*(s2+n);n++)
{
if(!*(s2+n+1))
return(char*)s1;
}
s1++;
}
return NULL;
}
else
return (char*)s1;
}
3. GCC-4.8.0
1
2
3
4
5
6
7
8
9
10
11
char *strstr(const char*s1,const char*s2)
{
const char*p=s1;
const size_tlen=strlen(s2);
for(;(p=strchr(p,*s2))!=0;p++)
{
if(strncmp(p,s2,len)==0)
return (char*)p;
}
return(0);
}
应用举例
// strstr.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include
#include
main()
{
char *s="GoldenGlobalView";
char *l="lob";
char *p;
clrscr();
p=strstr(s,l);
if(p)
printf("%s",p);
else
printf("NotFound!");
get);
return0;
}
//功能:从字串” string1 onexxx string2 oneyyy”中寻找”yyy”
(假设xxx和yyy都是一个未知的字串)
1
2
3
4
5
6
7
char *s=”string1onexxxstring2oneyyy”;
char *p;
p=strstr(s,”yyy”);
if(p!=NULL)
printf(“%s”,p);
else
printf("notfound ");
说明:如果直接写语句p=strstr(s,”one”),找到的是onexxxstring2oneyyy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
char *mystrstr(char*s1,char*s2)
{
if(*s1==0)
{
if(*s2)
return (char*)NULL;
return (char*)s1;
}
while(*s1)
{
int i=0;
while(1)
{
if(s2[i]==0)
return s1;
if(s2[i]!=s1[i])
break;
i++;
}
s1++;
}
return (char*)NULL;
}
C语言的一些误用和知识总结
具体如下:
1.关于自增自减(即++i,i++)
要想给一个数加一或减一我们可以:
i += 1;
j -= 1;
而C语言还允许用++和--运算符,其实这里有误导,因为++和--可以作为前缀和后缀,这样的话他们就有可能改变操作数的值,下面让我们来看看:
i = 1;
printf("i is %d ",++i); /* prints i is 2 */
printf("i is %d ",i); /* prints i is 2 */
计算表达式i++的结果是i,但是会引发i随后进行自增:
i = 1;
printf("i is %d ",i++); /* prints i is 1/ */
printf("i is %d ",i); /* prints i is 2 */
第一个printf 显示了i自增前的原始值,第二个printf显示了i变化后的新值;当然 -- 类似我就不举例了~
但在同一个表达式中多次使用++和--往往很难理解我们看看下面的例子:
i = 1;
j = 2;
k = ++i + j++;
i,j,k最终值分别是2,3,4而++i是2 j++是2;
总结:不管是++i还是i++执行这条语句后i的值都加一了只是(++i)的值加一了而(i++)没变,
2.typedef与#define
2.1.typedef
C语言除了直接使用标准的类型名(如 int char float double)和自己声明的结构体、共用体、指针、枚举类型外,还可以用typedef声明新的类型名来代替现有的类型名。
typedef unsigned char u8;
typedef unsigned int u16;
u8 count;
u16 time;
typedef struct
{
u8 month;
u8 day;
u16 year;
}DATE;
DATE brithday;
总结一下,声明新的类型名的方法:
1.先按定义变量的方法写出定义体(如 unsigned int i)
2.在变量名换成新的变量名(如将 i换成u16)
3.在最前面加上typedef (typedef unsigned int u16)
4.然后用新类型名去定义变量
2.2#define
2.1.1不带参数的宏定义
define 标识符 字符串
define PI 3.1415926
注意:
1.它的作用是在本程序中用指定的标识符PI来代替3.1415926
2.宏定义是用宏来代替字符串也就是做简单的置换,不做正确性检查如果写成
define PI 3.l4l6926
即把1写成了字母l但是预处理照常代入不做任何语法检查!!
2.1.2带参数的宏定义
define 宏名(参数) 字符串
define S(a,b) a*b
area = S(a,b);
define MAX(x,y) (x)>(y) ? (x):(y)
3.typedef和#define的区别
一般来说typedef 因为它能正确处理指针类型
typedef char *String1;
define String2 char *
String1 s1,s2;
String2 s3,s4;
s1,s2,s3 被定义为了char* 但s4却被定义为了char型
3. static 变量
static变量大致分为三种用法
1. 用于局部变量中,成为静态局部变量. 静态局部变量有两个用法,记忆功能和全局生存期.
2. 用于全局变量,主要作用是限制此全局变量被其他的文件调用.
3. 用于类中的成员.表示这个成员是属于这个类但是不属于类中任意特定对象
静态局部变量
静态局部变量属于静态存储方式,它具有以下特点:
(1)静态局部变量在函数内定义 它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
(2)允许对构造类静态局部量赋初值 例如数组,若未赋以初值,则由系统自动赋以0值。
(3) 对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。
举例如下:
void fun()
{
static int a = 1;
a++;
}
在第一次进入这个函数的时候,变量a被初始化为1!并接着自增1,以后每次进入该函数,a就不会被再次初始化了,仅进行自增1的操作;在static发明前,要达到同样的功能,则只能使用全局变量:
int a = 1;
void fun()
{
a++;
}
静态全局变量
全局变量(外部变量)的之前再加上static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于,非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。
static的类成员变量
static关键字有两种意思,你看上下文来判断
1.表示变量是静态存储变量,表示变量存放在静态存储区.
2.表示该变量是内部连接(这种情况是指该变量不在任何{}之内,就象全局变量那样,这时候加上static),也就是说在其它的.cpp文件中,该变量是不可见的(你不能用)。
static 函数 —— 内部函数和外部函数
当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。
1 内部函数(又称静态函数)
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。
定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:
static 函数类型 函数名(函数参数表)
{……}
关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。
2 外部函数
外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
[extern] 函数类型 函数名(函数参数表)
{……}
调用外部函数时,需要对其进行说明:
[extern] 函数类型 函数名(参数类型表)[,函数名2(参数类型表2)……];
【C语言字符型基础知识】相关文章:
C语言字符串04-14
C语言基础知识12-20
c语言入门基础知识07-18
C语言位运算基础知识02-20
C语言的基础知识点复习08-18
怎样学习c++c语言编程04-28
C语言的基本构成12-05
c语言的优点介绍04-13
C语言试题训练10-20
C语言入门知识07-20