黑马程序员--02C数组/字符串/指针

                                                 ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

  1.数组


数组是一种构造类型.

数组用来存储一组相同类型的数据.可以是任何类型的.但必须是同一种类型的数据,存放的数据被称为元素。


格式: 类型数组名[元素个数];

int a[5];

错误写法:int a[];       //定义时不可以不指定元素个数

初始化 

初始化是指在数组定义的同时给数组元素赋值.

注意

只有在定义时,并且元素个数是常量[表达式],才可以用{}初始化,未被初始化的元素会被自动赋值为0.

(常量可以是 数字, #define的宏, const变量,字符常量等).

 

初始化的方式 

a.int a[5] = {1, 2, 3, 4, 5};

b.int a[] = {1, 2, 3, 4, 5};初始化时可以省略元素个数,相当于 int a[5] = {...};

c.int a[5] = {1, 2};初始化部分元素:只给前两个元素赋值,其它元素默认为0.

d.int a[5] = {[3] = 4, [4] = 5};初始化指定元素:只给a[3]a[4]赋值,其它元素默认为0

e.int a[5] = {};所有元素赋值为0

f.int a['A'-60] = {1, 2, 3};相当于 int a[5] = {...}; 

 

 错误写法 

a.  int a[5]; 

a = {1, 2, 3, 4, 5};      // 错误  {}初始化只能在定义时使用

定义后数组名就是个常量,不能被赋值。所以必须要逐个元素来赋值

b.. int n = 5;int a[n] = {1, 2, 3, 4, 5};   //错误

元素个数必须为常量或常量表达式时才能在定义时初始化.如果是变量或变量表达式,不能初始化

但是可以这样:int a[n];a[0] = 1; a[1] = 2;

这样是可以的,注意这样就不是初始化了,没有赋值的元素的值不是0,而是任意数.

数组元素的赋值

a[0] = 1;a[1] = 2;

注意:

如果数组经过{}初始化后,没有被主动赋值的元素值为0.如果是在定义后逐个赋值,没有被主动赋值的元素值可能为任意值.

C语言是弱语法,编译器不会对数组下标越界检查,自己要注意。

 

 

计算数组元素个数

int n = sizeof(a) / sizeof (int);

注意

如果把数组名作为函数参数,在函数内部是无法获得数组的元素个数的。因为函数参数接收的是数组的地址,所以使用sizeof(a)打印出的是指针的大小8个字节,而不是整个数组所占的字节数。所以必须要把数组中元素的个数作为参数传递给函数。


数组元素的遍历

遍历:按顺序查看数组的每一个元素

for (i = 0; i < n; i++)

          printf("a[%d] = %d\n", i, a[i]);

 

 

数组在内存中的存储


数组名

a.数组名就是数组的地址

b.数组名是个常量,不能被赋值(所以数组在定义后就不可以使用{ }赋值,只能逐个元素的赋值)

数组占用的内存空间

a.数组在内存中存储为一段连续的内存空间,比如一个int a[2]的数组,所占空间为4 * 2 = 8个字节。

b.sizeof(a) 来看数组占用字节数

数组的地址和它的每个元素的地址

a.数组中的元素在内存中按照由小到大的地址排列

b.数组的地址是它的起始地址(最小地址),等于数组名的值

c.可以使用 prinf("%p", &a[i]);来查看元素/数组在内存中的地址


数组与函数 


使用数组作为函数参数

由于数组名代表的是数组的地址,所以形参复制了实参的地址,和实参指向同一个数组,修改形参数组中的内容能够修改实参数组中的内容。

void change (int a[])

 {    // 使用数组作为函数参数

a[0] = -1;

}

change(a);    // 传递数组地址

注:形参可以不写元素个数,但是[]必须要写,表示参数是数组类型。实参只写数组名,不用写[]

传递的是数组名,即数组的地址,所以函数操作的和实参是同一个数组,能够修改原数组元素的值.


使用数组元素作为函数参数

由于数组元素是基本类型变量,只是简单的值传递,修改形参不改变原数组元素的值

void change(int n) {   // 使用基本数据类型作为函数参数

n = -1;

}

change(a[0]);     // 不改变参数的值

参数是基本数据类型变量,只是简单的值传递.

 

函数与数组元素的个数

比如 int change(array[]){   };

 我们把数组名传递到函数中,在函数中再打印数组名的size,会发现打印出的结果总是8个字节.这是因为传递到函数中的其实是个数组指针,所以sizeof(array)返回的是数组指针在内存中占据的空间大小,而不是实参数组所占据的空间.

所以在C语言中,如果函数需要知道数组大小,传递数组地址的同时,还需要传递给函数这个数组中元素的个数.



   2.字符串

字符串其实就是一个以\0结尾的字符数组.

字符串通常以""表示,使用""会自动在后面加'\0'字符

组成

字符串由很多个字符组成,以字符'\0'结束.

初始化

字符串就是一个以'\0'结束的字符数组

a.char name[10] = {'J', 'a', 'c', 'k', '\0'};  

b.char name[10] = {'J', 'a', 'c', 'k', 0};因为'\0'ASCII码值为0

注意

a.char name[10] = {'a', 'b'};

也是一个字符串,因为字符数组默认后面的值都是0 ('\0')

 b.但是 

char name[2] = {'a', 'b'}; 

char name[] = {'a', 'b'};就不是字符串了,因为它们只有两个元素,不以\0结束.

字符串都是字符数组,但不是所有的字符数组都是字符串.

c.通常还是使用双引号来定义

char name[10] = "Jack";     //使用双引号会在末尾自动添加'\0'

char name[] = "Jack";         //可以不指定数组大小

 

\0的作用

如果定义一个字符串和一个普通的字符数组,如果我们用字符串方法操作字符输出(比如用%s输出),会发现把前面的字符串都输出出来了.

char name[] = "abc";

char name2[] = {'d', 'e'};

printf("%s\n", name2);

printf函数打印字符串的原理

printf函数中使用name2作为参数,这个数组名其实就是传递了数组name2的地址, printf("%s",a)这个函数就是从提供的地址开始输出字符,一直到\0结束输出.所以,如果一个字符串不以'\0'结束,那么就会一直打印,直到遇到一个'\0'为止,如果打印到未被赋值的内存区域,就会打印出乱码。

 

字符串函数 strlen

这个函数计算一个字符串的字符个数,不包括'\0',是在<string.h>中声明的

#include <string.h>

strlen("Jack");            //返回 4

strlen("Jack");       //返回 7,一个汉字占三个字符

注意

a.sizeof区分, sizeof计算占的字节数(包括字符串中的'\0'), strlen计算字符个数

b.strlen()函数也是从给的字符串地址开始计算字符数,直到遇到'\0'为止


字符串的遍历

编写一个函数来判断一个字符串中是否包含某个特定字符,如果包含就返回1,否则返回0.

 

使用for循环

#include <string.h>
int contains_char(char str[], char c) {
for (int i= 0; i < strlen(str); i++){
if (str[i] == c)
return 1;
}
return 0;
}

使用while 循环

i

int contains_char(char str[], char c) 
{
int i = 0;
while(str[i] != '\0') 
{       //可以写成while(str[i]) 因为'\0'的值是0, 不是0就是真值.
if (str[i] == c)
return 1;
i++;
}
return 0;
}

 

字符串数组

装字符串的数组,比如储存多个人的名字:

char names[3][10] = {

"Jack",

"Maryl",

"Den"

};

printf("The second person is %s\n", names[1]);       //获取其中一个字符串

printf("The last name ends with letter %c\n", names[2][3]);    //获取其中某个字符串中的一个字符

 

3.指针

指针变量


作用存储内存地址的变量.根据这个地址就可以操作(获取或修改)这块存储空间中的内容.

格式 类型标识符  *变量名int *p;

 指针可以用于指向任何类型的变量(比如数组,结构体,其它指针等),所以需要定义同种类型的指针

初始化int *p;  

p = 10;

或者int *p = 10;

注意指针变量只能用来存地址

 指针没初始化不要随便用来访问存储空间


指针的加法

在数组中常常会使用*(p+i)这种形式,指针可以和数字相加,指针的类型决定了当指针+1时跳过多少个字节。比如int 4 ,double  8, char 1.....

 

指针变量占的空间大小

不管指针指向什么类型的数据,它存储的都是内存地址。所以所有的指针变量所占的空间都是一样的,与指针变量的类型无关,只跟编译器有关。在Xcode中,指针变量占8个字节

 

 

指针与数组

利用指针来接收一个数组,指针变量指向的了数组的首元素(数组的地址)


数组元素的访问方式

a.数组名[下标];age[i];

b.指针变量[下标];p[i];

c.*(age+i)

d.*(p+i) == *(age+i)//注意  age为指针常量   p为指针变量(p++

 

数组指针和函数参

如果一个函数要操作一个数组,可以使用数组作为函数参数,也可以使用数组指针作为函数参数,二者是等价的,都可以修改数组中的元素。

调用的时候,实参都是数组名,因为数组名就是数组首元素的地址。

 

使用数组作为函数参数

void change ( int array[]) 

{      //形参是数组 实际为 int *array  接收一个地址  所以两者等价

// int *  说明其只能指向一个数组元素

array[0] = 100;    //修改数组的元素

}

int a[3]={1,2,3};

change(a);         //实参是数组名(指针,即是个地址)

当函数的参数是个数组的时候,调用时实参可以是数组名,也可以是指向数组(数组元素)的指针

 

字符串指针


字符串的定义

利用数组   char name[] = "itcast";

特点:这样定义的字符串其实是一个变量字符串,里面的字符是可以修改的。

 利用指针   char *name = "itcast";

特点:这样定义的字符串其实是一个常量字符串,里面的字符是不能修改的。

字符指针数组

定义一个字符指针数组:

char *name[2] = {"jack","rose"};

其实用字符串数组也可实现上面的存储:

char name[2][10] = {"jack","rose"};

注意

char s[5]; char *p;

s = "abc"; p = "abc";

错误!s是数组首地址,是个常量 正确!p是字符指针,可以指向一个字符串。


char s[5]="abc"  正确!s是一个字符数组,可以在定义的时候初始化,这个"abc"是一个字符串变量。 

char *p = "abc";  正确!p是一个字符指针,可以指向一个字符数组,这个"abc"是一个字符串常量。

 

char *p = "abc";

*p = "abcd";错误!*p指向的"abc"是一个字符串常量,不可以被重新赋值。

 

char *p = "abc";

p = "abcd";  正确!可以把p指向一个新的字符串(也是常量)。

 

char s[5] = "abc";

s[0] = 'A';   正确!字符数组指向的是可变字符串,可以修改字符串中的内容。

 

char *p = "abc";

p[0] = 'A';  错误!字符指针指向的是不可变字符串,不能修改字符串中的字符。

 

char s[] = "abc";

char *p = s;

p[0] = 'A'; 正确!p虽然是个指针,但是它现在指向的是s[]定义的可变字符串,所有可以修改字符串的内容。

 

小结:

用数组形式s[]定义的是字符串变量,可以像数组一样访问,可以修改字符串的内容,比如s[0] = 'A'

用指针形式*p定义的是字符串常量,不可以修改字符串的内容(比如p[0] = 'A'; *p = "ABC";),但是可以指向一个新的字符串(比如 p = "ABC";),也可以指向一个可变字符串,比如p = s;就可以修改这个字符串中的内容了 p[0] = 'A';  如果再次指向一个新定义的字符串(比如 p = "abc"),那还是字符串常量,不可修改。


使用场合

如果字符串的内容需要被修改,使用char[]方式定义字符串变量

如果字符串的内容经常使用但不需要修改,使用char*方式定义字符串常量。

 

 

函数指针


指向函数的指针

定义形式:函数的返回值类型 (*指针变量名)(形参1,形参2, ...);

比如int sum (int a, int b) 

{int c = a  + b;

return c;}

可以定义一个同类型的指针:

int (*p)(int, int) ;    //这样定义函数指针,用(*p)来代替函数名的位置

p = sum;       // 函数名就是函数的地址,p获得了sum函数的地址,指向这个函数

注意

        a.使用指针变量p来间接调用sum函数:

(*p)(5, 6);           //小括号不可省略

 p(5, 6);               //相当于sum(56


b.由于这类指针变量存储的是一个函数的入口地址,所以对它们作加减运算(比如p++)是无意义的

c.指向函数的指针变量主要有两个用途:调用函数,将函数作为参数在函数间传递

返回指针的函数

类型名 *函数名(参数列表)

char *test ()

{

return "rose";

}

int main()

{

char *name = test();

return 0

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值