非典型性C语言教程-1.4 指针,字符串,数组
2007-07-06 11:11
351 查看
指针应该是C语言中比较难的一个概念,也是C语言之所以被称为中级语言而不是高级语言的原因。程序离不开内存,程序本身的代码也是在内存中,程序要处理的 数据也是在内存中。内存是有地址的,目前大多数计算机的内存都是以8bits也就是一个byte为单位编排地址的。指针就是一个内存地址,C语言中所有在 内存中有地址的东西都有相应的指针。
首先是指针的类型。一种类型的指针可以定义为type * p;的形式。虽然可以定义各种指向不同类型变量的指针,但是指针就是内存的一个地址,也就是一个无符号的整数。需要注意的是不同的机器上指针的大小是不一 样的。比如在16位机器上一个sizeof(p)是2个字节,32位机器上则是4个字节,而在64位机器上可能是8个字节。但是有一个一般性的规律,就是 指针的代销一般是和long类型的大小一样,也就是sizeof(*p)=sizeof(long)。由于指针本身在内存中也是一个无符号的整数,所以也 有指向指针的指针,比如int** p。但是只要记住指针是一个地址它和一个无符号整数类似。清楚了基本概念就好理解。
你会说既然指向每 种类型的指针都是一样的,那么为什么要定义那么多种类型的指针呢?区别在于不同类型的指针做加减运算时的行为是不一样的。指针可以做加减运算,比如p+ +, p=p+2等等,如果p是这么定义的type* p;那么p+n的解释成(long)p+sizeof(type)*n。也就是说指针加减的运算以指向内容的大小为基础,以保证*(p+1)指向的应该还 是一个正确的类型,而不会指向变量的一半内容。有一种特殊类型的指针就是void* p,这种指针没有指向内容的具体类型,可以指向任意的内存地址,这种指针是不能进行加减运算的,因为sizoof(void)什么也求不出来。
C语言是没有数组这种类型的。你可以定义数组char ee[20];但是没有数组类型,比如你不能定义一个函数返回数组类型。C语言的数组其实只是指针的一个简单的变化。比如
char ee[20];//定义了一个数组
ee[0]=0;
ee[2]=2;
定义数组ee的时候ee的实际类型是char* const,即是一个常量指针,不能修改的指针。引用数组元素ee[2]实际变成了*(ee+2)这样的形式。注意ee+2是合法的表达式,但是ee=ee+2就不合法了,因为ee是一个常量指针。有时候可能遇到诸如
void foo(char[] x);
这样的函数定义,这个函数定义和
void foo(char*x);
是没有区别的,关于数组大小的信息不能传递到函数里面去。所以正确的定义方式应该是
void foo(char*x, unsigned int num);
然后调用的时候传递参数
foo(ee, sizeof(ee)/sizoef(char));
这样就把数组元素的个数专递进去了。
还有一个概念是字符串,比如
char*p ="Hello world!";
但 是和数组一样,C语言同样没有字符串这种类型。字符串是一种字符 char类型的数组,但是以0结尾。上面这种写法实质是在内存的常量区域分配了一个字符数组的空间是13个元素(别忘了结尾还有一个0),然后在栈上分配 了一个指针的空间,让这个指针指向常量区域的那个字符数组。printf("hello world!/n");这句话实质上的过程是一样的,只是没有明确的定义出p,隐含的把p当作参数传递给了printf函数。结尾的那个隐含的0是非常重要的。C 语言中所有的操作字符串的标准函数比如strlen,strcpy, 还有printf的%s,都依赖于结尾隐含的0,0表示字符串的结束。如果没有找到这个0,会一直找到内存中的0位置,这一般不是我们要的结果。英文叫 Null-Terminated Strings。我曾今见过一个错误就是没有注意结尾的0造成的。
原本的意思是分配一块足够大的char数组,然后通过拷贝把3个字符串连接到一起。代码是这样写的
memcpy(buff, "hello world!", sizeof("hello world!"));
buff+=sizeof("hello world!");
memcpy(buff, "sttony!", sizeof("sttony"));
.........
这段代码有点丑陋但是也可以完成工作出问题的是 sizeof("hello world!")。sizeof("hello world!")会得出长度是13。结果在buff数组里面成了这样的内存布局:
hello world!/0sttony/0
然后后来用printf("%s", buff);怎么也打不出后面的字符串,只能打出第一个。
正确的做法应该是
strlen("hello world!");
这样就能得到长度是12。
指针也是一种变量,变量也分为全局,局部,和堆变量。指针也可以,也应该是指向一个变量的,指向的变量可能是全局,局部或堆变量。
char* p;
*p='a';
这种代码就是指针没有指向任何变量,黑话叫"野指针"。这种是很低级的错误了,比较隐晦的错误是
char* foo()
{
char a='a';
return &a;
}
.....
char*p =foo();
p指向foo局部变量a。但是函数的栈变量(局部变量)在函数结束的时候就属于未知的内存了,p也成一个野指针。还有这样的错误
char*p= (char*)malloc(250*sizeof(char));
........
free(p);
......
*p='d';
同样的,p指向的是堆上分配的堆变量空间,但是free之后,堆变量已经输入未知状态了。p也已经成了一个野指针。
下一回讲最复杂的函数指针。
首先是指针的类型。一种类型的指针可以定义为type * p;的形式。虽然可以定义各种指向不同类型变量的指针,但是指针就是内存的一个地址,也就是一个无符号的整数。需要注意的是不同的机器上指针的大小是不一 样的。比如在16位机器上一个sizeof(p)是2个字节,32位机器上则是4个字节,而在64位机器上可能是8个字节。但是有一个一般性的规律,就是 指针的代销一般是和long类型的大小一样,也就是sizeof(*p)=sizeof(long)。由于指针本身在内存中也是一个无符号的整数,所以也 有指向指针的指针,比如int** p。但是只要记住指针是一个地址它和一个无符号整数类似。清楚了基本概念就好理解。
你会说既然指向每 种类型的指针都是一样的,那么为什么要定义那么多种类型的指针呢?区别在于不同类型的指针做加减运算时的行为是不一样的。指针可以做加减运算,比如p+ +, p=p+2等等,如果p是这么定义的type* p;那么p+n的解释成(long)p+sizeof(type)*n。也就是说指针加减的运算以指向内容的大小为基础,以保证*(p+1)指向的应该还 是一个正确的类型,而不会指向变量的一半内容。有一种特殊类型的指针就是void* p,这种指针没有指向内容的具体类型,可以指向任意的内存地址,这种指针是不能进行加减运算的,因为sizoof(void)什么也求不出来。
C语言是没有数组这种类型的。你可以定义数组char ee[20];但是没有数组类型,比如你不能定义一个函数返回数组类型。C语言的数组其实只是指针的一个简单的变化。比如
char ee[20];//定义了一个数组
ee[0]=0;
ee[2]=2;
定义数组ee的时候ee的实际类型是char* const,即是一个常量指针,不能修改的指针。引用数组元素ee[2]实际变成了*(ee+2)这样的形式。注意ee+2是合法的表达式,但是ee=ee+2就不合法了,因为ee是一个常量指针。有时候可能遇到诸如
void foo(char[] x);
这样的函数定义,这个函数定义和
void foo(char*x);
是没有区别的,关于数组大小的信息不能传递到函数里面去。所以正确的定义方式应该是
void foo(char*x, unsigned int num);
然后调用的时候传递参数
foo(ee, sizeof(ee)/sizoef(char));
这样就把数组元素的个数专递进去了。
还有一个概念是字符串,比如
char*p ="Hello world!";
但 是和数组一样,C语言同样没有字符串这种类型。字符串是一种字符 char类型的数组,但是以0结尾。上面这种写法实质是在内存的常量区域分配了一个字符数组的空间是13个元素(别忘了结尾还有一个0),然后在栈上分配 了一个指针的空间,让这个指针指向常量区域的那个字符数组。printf("hello world!/n");这句话实质上的过程是一样的,只是没有明确的定义出p,隐含的把p当作参数传递给了printf函数。结尾的那个隐含的0是非常重要的。C 语言中所有的操作字符串的标准函数比如strlen,strcpy, 还有printf的%s,都依赖于结尾隐含的0,0表示字符串的结束。如果没有找到这个0,会一直找到内存中的0位置,这一般不是我们要的结果。英文叫 Null-Terminated Strings。我曾今见过一个错误就是没有注意结尾的0造成的。
原本的意思是分配一块足够大的char数组,然后通过拷贝把3个字符串连接到一起。代码是这样写的
memcpy(buff, "hello world!", sizeof("hello world!"));
buff+=sizeof("hello world!");
memcpy(buff, "sttony!", sizeof("sttony"));
.........
这段代码有点丑陋但是也可以完成工作出问题的是 sizeof("hello world!")。sizeof("hello world!")会得出长度是13。结果在buff数组里面成了这样的内存布局:
hello world!/0sttony/0
然后后来用printf("%s", buff);怎么也打不出后面的字符串,只能打出第一个。
正确的做法应该是
strlen("hello world!");
这样就能得到长度是12。
指针也是一种变量,变量也分为全局,局部,和堆变量。指针也可以,也应该是指向一个变量的,指向的变量可能是全局,局部或堆变量。
char* p;
*p='a';
这种代码就是指针没有指向任何变量,黑话叫"野指针"。这种是很低级的错误了,比较隐晦的错误是
char* foo()
{
char a='a';
return &a;
}
.....
char*p =foo();
p指向foo局部变量a。但是函数的栈变量(局部变量)在函数结束的时候就属于未知的内存了,p也成一个野指针。还有这样的错误
char*p= (char*)malloc(250*sizeof(char));
........
free(p);
......
*p='d';
同样的,p指向的是堆上分配的堆变量空间,但是free之后,堆变量已经输入未知状态了。p也已经成了一个野指针。
下一回讲最复杂的函数指针。
相关文章推荐
- 指针和数组、字符串 | C语言教程 | C语言系列教程
- C_PlusPlus学习笔记 - 5_数组、指针和字符串 (C++语言程序设计【第三版】 郑莉等,清华大学出版社)
- C 语言中指针、字符串与数组的一些关系
- C语言基础知识之(十四):指针和字符数组、字符串数组
- Swift3.0语言教程使用指针创建和初始化字符串
- 黑马程序员 _4 C语言基础 数组,指针,字符串
- C语言基础:指针类型与指针和数组、字符串的关系
- 字符数组和字符串 | C语言教程 | C语言系列教程
- c语言中 字符数组与字符串字面值 字符数组与字符指针数组
- c语言基础2—数组、字符串、指针
- Swift3.0语言教程使用指针创建和初始化字符串
- 非典型性C语言教程-1.5 函数指针
- 主题四 指针和数组(上)----23.C语言中的字符串
- Swift3.0语言教程使用指针创建和初始化字符串
- 4-数组、指针与字符串1.4-动态内存分配
- 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)
- C语言学习之 数组,指针,字符串. (三)
- C语言基础之数组、字符串、指针
- 黑马程序员----C 语言学习笔记之数组指针与字符串指针
- c语言基础--数组和指针---字符串