指针与数组
数组名
数组名是数组首元素的地址,数组名是一个常量不可以进行修改·。
数组名的地址 == 数组的首元素地址 == 整个数组的地址;(地址的值是相当的,但意义是不同的)
注意:
数组名在 sizeof(数组名); eg: sizeof(arr);
数组名在&数组名; eg &arr;
在这两种情况下数组名代表的是整个数组;其他情况下数组名代表的都是数组的首元素地址。
数组名不可以++ , 但是指针变量可以 p++;
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个数组
int arr[5] = {1,2,3,4,5};//arr 数组名
printf("数组首元素地址:%p\n",&arr[0]);
printf("数组名arr = %p\n",arr);
//数组名的值是一个地址,地址的值与数组的首元素的值是一样的
//通过*取出数组名里面的值
printf("*arr = %d\n",*arr);//arr是一个地址, *运算符
//获取整个数组的地址
printf("&arr = %p\n",&arr);
return 0;
}
结果:
数组首元素地址:0x7ffe98e777a0
数组名arr = 0x7ffe98e777a0
*arr = 1
&arr = 0x7ffe98e777a0
课堂练习
要求:使用 * 运算符 配合数组名称,遍历下面的数组。
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个数组
int arr[10] = {1,4,2,5,3,6,7,9,8,0};
//通过数组名遍历数组
for(int i = 0;i < 10; i++)
{
//arr + 0 是数组的首元素的地址,下一个元素arr + 1
//printf("%d ",*(arr + i)); √
//printf("%d ",*arr + i); ×
//printf("%d ",*(arr++)); × 含义:arr++ ==> arr=arr+1
//常量不可以被赋值
printf("%d ",arr[i]);//√ 通过下标进行访问
//总结:arr[i] 与 *(arr + 1)都可以访问
//[]运算符:下标运算符 实现方式:变量名[下标] = *(变量名 + 下标);
}
putchar(10);
return 0;
}
结果:
1 4 2 5 3 6 7 9 8 0
指针与一维数组
数组名 是数组的首元素地址;通过指针保存数组的地址;
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个一维数组
int arr[10] = {1,2,3,4,0,9,8,7,6,5};
//数组名是数组的首元素地址
int *p = arr;
//通过针对对数组进行遍历
for(int i = 0;i < 10;i++)
{
//printf("%d",*(p + i));//p 是一个指针 指针+1 移动到下一位
//printf("%d",p[i]);//[i] <==> *(+i)
printf("%d ",*p++);//单目运算符从右向左进行运算 先运算++
//++是后置运算符,后生效
}
putchar(10);
return 0;
}
结果:
1 2 3 4 0 9 8 7 6 5
数组越界问题
数组里面进行赋值的时候,超过了数组所申请的个数;
int arr[5] = {1,2,3,4,5,6};//error越界
//遍历数组
int arr[5] = {1,2,3,4,5};
for(int i = 0;i < 10;i++)
{}
越界:
在代码编译的过程中,编译器是不会检测到越界问题,检测越界问题需要耗费的资源太大;
越界写:越界写有可能会报错,也有可能不报错·;
越界读:一般不会发生错误,除非有些区域禁止阅读。
指针与字符串
字符串表示方法:
(1)使用数组的形式记录字符串:char mystr[128] = "hello world";
(2)使用指针的形式记录字符串:char *mystr = "hello world";
"hello world"内存中 .ro段
mystr在内存中 栈区:内存中定义了一个指针类型变量,
变量里面的内容是 .ro段里面"hello world"字符串的
地址。
计算机内存
在linux虚拟机里,虚拟硬件中内存的大小:4G (0x00000000 ~ 0xffffffff)的空间。
#include <stdio.h>
int main(int argc, const char *argv[])
{
//定义一个变量
int number = 10;//变量number定义在内存中
//打印变量在内存中的地址
printf("number 的地址: %p\n",&number);//物理内存4G 0x00000000~0xffffffff
return 0;
}
结果:
number 的地址: 0x7ffff761f774
虚拟内存
C语言中,所有能够接触到的地址,全部都是虚拟地址,不是真正意义上的物理内存的地址。
虚拟地址的优点:
(1)在物理意义上防止了野指针的问题
(2)每一个人的计算机内存大小不一样(4G,8G,16G),统一使用4G的虚拟内存可以方便管理。
(3)0G ~ 3G 每一程序都会有独立的用户空间。
3G ~ 4G所有程序共用一个内核空间。
虚拟内存与物理内存映射
#include <stdio.h>
#include<stdlib.h>
int a;
int b = 10;
static int c = 10;
void function()
{
int d = 10;
printf("hello world\n");
}
int main(int argc, const char *argv[])
{
char *f = "hello world";
char g[20] = "hello";
int *h = malloc(20);//malloc的返回值是一个指针
return 0;
}
上述代码中:分别存到哪一个区域
a :未初始化的全局变量:.bss段
b :初始化的全局变量:.data段
c :static修饰的全局变量:.data段
d :初始化的局部变量:栈区
function :.text段
f :局部变量:栈区
*f :相当于字符串“hello world” 存在.ro段
g :局部变量:栈区
*g :char g[20] = "hello";在栈区中申请20个字节空间,空间里面存放的是h e l l o
h :h是一个局部变量,所以在栈区
*h :malloc申请的空间 :堆区
数组与函数传递参数问题
数组名作为函数参数传递的时候:会降级为指针;
所以在函数定义的时候,如果需要传输数组,函数的参数就是指针类型。
#include <stdio.h>
//定义函数
void show(int *arr,int size)//数组类型作为参数的时候,会降级为指针
{ //int arr[10] 降级为 int *arr 指针
for(int i = 0;i < size;i++)
{
printf("arr[%d] = %d\n",i,arr[i]);
}
}
int main(int argc, const char *argv[])
{
//定义一个数组
int array[10] = {1,2,3,4,5,0,9,8,7,6};
//定义一个函数,遍历数组
show(array,10);
return 0;
}
结果:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
arr[5] = 0
arr[6] = 9
arr[7] = 8
arr[8] = 7
arr[9] = 6
常见的字符操作函数
c语言库中为我们准备了很多的字符串操作函数,需要头文件 #include <string.h>
strlen函数
函数原型:
size_t strlen(const char *s);
函数功能:
计算字符串的长度;
函数参数:
需要测量长度的字符串;
返回值:
size_t 类型就是 unsigned int 类型
返回值就是字符串的长度。
#include <stdio.h>
#include <string.h>
//定义一个函数返回字符串的长度
int mystrlen(char *s)
{
//定义一个计数器
int count = 0;
while(*s != '\0')
{
count++;
s++;
}
return count;
}
int main(int argc, const char *argv[])
{
//定义一个字符串
char mystr[128] = "hello world";
char *mystr1 = "hello";//也是一个字符串
int len = strlen(mystr);
int len1 = mystrlen(mystr1);
printf("len = %d,len1 = %d\n",len,len1);
return 0;
}
strcpy函数
函数原型:
char *strcpy(char *dest, const char *src);
函数功能:
将原字符串的内容拷贝到目标字符串;
函数参数:
dest:目标字符串
src:原字符串
返回值:
目标字符串的首地址;
#include <stdio.h>
#include <string.h>
char *mystrcpy(char *dest,char *src)
{
//将src里面的内容 赋值给dest
char *p = dest;
char *q = src;
/*
//完成字符串的赋值
while(*q != '\0')
{
*p = *q;
p++;
q++;
}
*p = '\0';
*/
while(*p++ = *q++);//依次取出指针p里面的数据
//然后赋值给 指针p指向的空间,当*q为\0的时候,会将\0赋值给
//*p的位置,因为赋值的是\0,所以这个表达式的结果为\0
//0就是'\0',就是假,所以结束循环
}
int main(int argc, const char *argv[])
{
char *src = "hello world";
char dest[128];//空间必须给够
char *ch = strcpy(dest,src);
printf("ch = %s\n",ch);
printf("dest = %s\n",dest);
return 0;
}
结果:
ch = hello world
dest = hello world