liujsim / blog

blog
https://github.com/liujsim/blog/issues
MIT License
1 stars 0 forks source link

C 存储器和指针 #2

Open liujsim opened 8 years ago

liujsim commented 8 years ago

存储器和指针

指针

指针做了两件事:避免副本和共享数据

深入挖掘存储器

printf("x保存在 %p\n", &x);

%p格式符将地址以16进制的格式输出,如果想要找出变量的存储器地址可以使用 & 运算符

计算机会为变量在存储器中分配空间;局部变量位于栈中;全局变量位于全局字段;指针只是一个保存存储器地址的变量;&运算符可以找到变量的地址;*运算符可以读取存储地址中的内容;*运算符可以设置存储器地址中的内容

数组变量

C语言中有一个叫sizeof的运算符,它能告知某样东西在存储器中占 多少字节,既可以对数据类型使用,也可以对某条数据使用。

sizeof(int)   //大多数返回4
sizeof(int*)  //返回8
sizeof("hello") //返回6

数组变量好比指针

当你创建了一个数组,数组变量就可以当作指针使用,它指向数组在存储器中的起始地址。当C语言在函数中看到这样一行代码时:

计算机会为字符串的每一个字符以及结束字符\0在栈上分配空间,并把首字符的地址和quote变量关联起来,代码中只要出现 这个quote变量,计算机就会把它替换成字符串首字符的地址。 其实,数组变量就好比一个指针:

printf("The quote 字符串保存在: p%\n",quote);

指针在32位操作系统中占4字节,在64位操作系统中占8字节。

在32位操作系统中,存储器地址以32位数字的形式保存,所以它叫32位操作系统。32位==4字节,所以64位操作系统要用8个字节来保存地址

要点

数组变量可以被用作指针

数组变量指向数组中第一个元素

如果把函数声明为数组,它会被当作指针处理

sizeof 运算符返回某条数据占用空间的大小

也可以对某种数据类型使用sizeof,例如 sizeof(int)。

QA

sizeof不是一个函数,它是一个运算符

区别:编译器会把运算符编译为一串只能;而当程序调用函数时,会跳到一段独立的代码中执行;所以程序是在编译期间计算sizeof确定存储空间的大小。

32位与64位

在32位操作系统中,存储器地址以32位数字的形式保存,所以它叫32位操作系统。32位==4字节,所以64位操作系统要用8个字节保存地址

demo:

#include <stdio.h>

int main(int argc, const char *argv[]) //char*指针的数组
{
int contestants[] = {1,2,3};
int *choice = contestants;  //字符串字符的第一个值
printf("数组的第一个值是%p\n",contestants);  //16进制指针地址
printf("数组的第一个值是%i\n",*choice);  //1
return 0;
}

数组变量可以用作指针,这个指针指向数组的第一个元素,也就是说除了方括号表示法,还可以用*运算符读取数组的第一个元素

 int drinks[] ={4,2,3};

 printf("第一单: %i 杯\n",drinks[0]);

 printf("第一单: %i 杯\n",*drinks);

drinks[0] 和 *drinks 是等价的

地址只是一个数字,所以可以进行指针算术运算,比如为了找到存储器中的下一个地址,可以增加指针的值。既可以用方括号加上索引值2来读取元素,也可以对第一个元素的地址加2:

printf("第三单: %i 杯\n",drinks[2]);
printf("第一单: %i 杯\n",*(drinks+2));

总之表达式drinks[i]和*(drinks + i)是等价的

为什么指针有类型

既然指针只是地址,为什么指针变量有类型?为什么不能用一种通用的类型变量保存所有指针

因为指针算术运算会暗渡陈仓。如果对 char 指针加1,指针会指向存储器中下一个地址,那是因为 char 就占1字节。如果是 int 指针呢? int 通常占4字节,如果对 int 指针加1,编译后的代码就会对存储器地址加4。

int nums[] ={1,2,3};
printf("nums 的地址是 %p\n",nums);
printf("nums+1 的地址是 %p\n",nums+1);

用指针输入数据

scanf()是怎么工作的?它接收一个指针,因为scanf()函数打算更新数组的内容,一 个想要更新变量的函数可不需要变量本身的值,它要的是变量的地址

scanf()会导致缓冲区溢出

如果忘了限制scanf()读取字符串的长度,用户就可以输入远远超出程序空间的数据,多余的数据会写到计算机还没有分配 好的存储器中

还可以用另一个函数来输入文本数据:fgets()。和scanf()函数一样,fgets()接收char指针,不同的是,必须给出最大长度

fgets()只允许向缓冲区 中输入一个字符串,而且只能是 字符串,不能是其他数据类型, 只能有一个缓冲区

注意:sizeof 返回变量占空间的大小,在上面的代码中food是数组变量,所以sizeof返回了数组的 大小;如果food是指针变量,sizeof仅仅会返回指针的大小。

字符串字面值不能更新

示例

#include <stdio.h>
int main()
{
    char *cards = "JQK";
    char a_card = cards[2];
    cards[2] = cards[1];
    cards[1] = cards[0];
    cards[0] = cards[2];
    cards[2] = cards[1];
    cards[1] = a_card;
    puts(cards);
    return 0;
} 

编译成功,运行出错。总线错误 bus error

指向字符串字面值的指针变量不能用来修改字符串的内容:

char *cards = "JQK" ;

但如果你用字符串字面值创建一个数组,就可以修改

char cards[] = "JQK";

为了从此避免这个错误,可以不再将char指针设置为字符串字面值

char *s ="Some string";

但是把指针设为字符串字面值又没错,问题出在你试图修改字符串字面值。如果你想把指针设成字符串字面值,必须确保使用了const关键字

const char *s = "some String"

修改数组编译期间报错

存储器提示卡

字符串

"数组的数组" 和 "指针的数组"

数组的数组:二维数组

指针的数组就是保存在存储器地址的数组,如果想要快速创建字符串字面值列表,指针的数组就非常有用

char *name_for_dog[] ={"Bowser","Bonza","Snodgrass"}

语法备注

#include <stdio.h>

    int main()
    {
        char mask[] = "Alive";
        char *jimmy = mask;
        printf("The Address is %s \n",jimmy); //The ... Alive
        printf("The Address is %p \n",jimmy);//The ... 0x7fff63bf1430 
        printf("The Address is %i \n",*jimmy);//The ... 65  //ASCI
        printf("The Address is %i \n",*jimmy+5);
        puts(jimmy);     //Alive
        return 0;
    }

char 型的指针指的是每个类型是 char 型,类似 Java 中的泛型 List,不是说将指针的值赋值给 char 型

声明 char 数组:

char mask[] = "Alive"  //  Yes

char[] mask = "Alive"  //No

声明数组长度

char info[80];