第8周 指针与字符串
1. 指针
1.1 取地址运算
运算符sizeof
是一个运算符,能够给出某个类型或变量在内存中所占据的字节数
sizeof(int)
sizeof(i)
🌰
#include<stdio.h>
int main()
{
int a;
a = 6;
printf("sizeof(int)=%d\n",sizeof(int));
printf("sizeof(a)=%d\n",sizeof(a));
return 0;
}
运算符&
scanf("%d",&i);
里的&
- 用来获得变量的地址,操作数必须是变量
int i; printf("%x", &i);
- 地址的大小是否与
int
相同,取决于编译器架构(32-bit or 64-bit)
int i; printf("%p",&i);
🌰
#include<stdio.h>
int main(void)
{
int i = 0;
int p;
p = (int)&i; // 强制整型
printf("0x%x\n", p);
printf("%p\n", &i); // 取地址
printf("%lu\n",sizeof(int));
printf("%lu\n",sizeof(&i));
return 0;
}
// 32-bit & 64-bit 会有区别
&
不能取的地址
&
不能对没有地址的东西取地址
&(a+b)
->?
&(a++)
->?
&(++a)
->?
🌰
#include<stdio.h>
int main(void)
{
int a[10];
printf("%p\n", &a);
printf("%p\n", a);
printf("%p\n",&a[0]); //取a[0]的地址
printf("%p\n",&a[1]); //取a[1]的地址
return 0;
}
1.2 指针
指针
int i;
int* p = &i;
int* p,q;
int *p,q;✅->`*p`为指针
指针变量
- 变量的值是内存的地址;
- 普通变量的值是实际的值;
- 指针变量的值是具有实际值的变量的地址;
作为参数的指针
void f(int*p)
;- 在被调用的时候得到了某个变量的地址:
int i=0; f(&i)
,在函数里可以通过该指针访问外面的i
;
🌰
#include<stdio.h>
void f(int *p);
int main(void)
{
int i =6;
printf("&i=%p\n",&i);
f(&i);
return 0;
}
void f(int *p)
{
printf(" p=%p\n",p);
}
访问那个地址上的变量
*
是一个单目运算符,用来访问指针的值所表示的低智商的变量
既可以表示左值也可以表示右值
int k = *p;->
*p = k+1;->
🌰
#include<stdio.h>
void f(int *p);
void g(int k);
int main(void)
{
int i =6;
printf("&i=%p\n",&i);//指的是i代表的地址
f(&i);
g(i);
return 0;
}
void f(int *p)
{
printf(" p=%p\n",p);
printf("*p=%d\n",*p);
*p = 26;//函数代表的是值的传递
}
void g(int k)
{
printf("k=%d\n",k);
}
1.3 指针与数组
1.3.1 传入函数的数组成了什么?
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes)
{
int ret = 1;
int i;
for( i=0; i<numberOfKnownPrimes; i++ )
{
if( x % knownPrimes[i] == 0 )
{
ret = 0;
break;
}
}
return ret;
}
1.3.2 Explore
🌰
#include<stdio.h>
void minmax(int a[], int len, int *max, int *min);
int main(void)
{
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));
minmax(a, sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min, max);
return 0;
}
void minmax(int a[], int len, int *max, int *min)
{
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
*min = *max = a[0];
for( i=1; i<len; i++ )
{
if( a[i] < *min )
{
*min = a[i];
}
else if( a[i] > *max )
{
*max = a[i];
}
}
}
🌰
#include<stdio.h>
void minmax(int a[], int len, int *max, int *min);
int main(void)
{
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));//
printf("main a=%p\n",a); // 查看a的地址
minmax(a, sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min, max);
return 0;
}
void minmax(int a[], int len, int *max, int *min)
// int a[]即为指针
{
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
*min = *max = a[0];
for( i=1; i<len; i++ )
{
if( a[i] < *min )
{
*min = a[i];
}
else if( a[i] > *max )
{
*max = a[i];
}
}
}
🌰
#include<stdio.h>
void minmax(int *a, int len, int *max, int *min);
int main(void)
{
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));//
printf("main a=%p\n",a); // 查看a的地址
minmax(a, sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min, max);
return 0;
}
void minmax(int *a, int len, int *max, int *min)
{
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
*min = *max = a[0];
for( i=1; i<len; i++ )
{
if( a[i] < *min )
{
*min = a[i];
}
else if( a[i] > *max )
{
*max = a[i];
}
}
}
1.3.3 小结
-
因此函数参数表中的数组其实是指针;
-
sizeof(a) == sizeof(int*)
; -
尽管如此,可以用数组的运算符
[]
进行运算;
1.3.4 数组参数
下列四种函数原型是等价的
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
1.3.5 数组变量是特殊的指针,
- 数组变量本身表达地址,所以:
1.1 `int a[10]; int *p=a;` // 无须使用`&` 取地址;
1.2 但是数组的单元表达的是变量,需要用`&`取地址;
1.3 `a == &a[0]`
[]
运算符可以对数组做,也可以对指针做:
2.1 `p[0] <==> a[0]`
*
运算符既可以对指针做,也可以对数组做;
3.1 例如`*a=25`;
- 数组变量其实是
const
(常量)的指针,因此不能被赋值;
4.1 `int a[] <==> int * const a=`
🌰
#include<stdio.h>
void minmax(int *a, int len, int *max, int *min);
int main(void)
{
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));//
printf("main a=%p\n",a); // 查看a的地址
minmax(a, sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min, max);
int *p = &min;
printf("*p=%d\n",*p);
printf("p[0]=%d\n",p[0]);
// p[0] 指 p 这个数组的第一个单元,这是一个指针变量,因此可如此书写
return 0;
}
void minmax(int *a, int len, int *max, int *min)
{
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
*min = *max = a[0];
for( i=1; i<len; i++ )
{
if( a[i] < *min )
{
*min = a[i];
}
else if( a[i] > *max )
{
*max = a[i];
}
}
}
2. 字符类型
2.1 字符类型
char是一种整数,也是一种特殊的类型:字符。
- 用单引号表示的字符字面量:‘a’,‘1’;
- ''也是一个字符;
Printf
和Scanf
里用%c
来输入输出字符
🌰
#include<stdio.h>
int main()
{
char c;
char d;
c = 1;
d = '1';
if( c == d )
{
printf("相等\n");
}
else
{
printf("不相等\n");
}
printf("c=%d\n",c);
printf("d=%d\n",d);
return 0;
}
2.2 字符的输入输出
如何输入‘1’这个字符给char c
?
scnaf("%c",&c);
->1scanf("%d",&i);c=i
->49
🌰
#include<stdio.h>
int main()
{
char c;
scnaf("%c",&c);
printf("c=%d\n",c);
printf("c='%c'\n",c);
return 0;
}
2.3 混合输入
2.3.1 有何区别?
scanf("%d %c",&i,&c);
scanf("%d%c",&i,&c);
🌰
#include<stdio.h>
int main()
{
int i;
char c;
scnaf("%d %c",&i,&c);
printf("i=%d,c=%d,c=`%c`\n"i,c,c);
return 0;
}
2.3.2 字符计算
char c = 'A';
c++;
printf("%c\n",c);
🌰
#include<stdio.h>
int main()
{
char c = 'A';
c++;
printf("%c\n",c);
return 0;
}
int i = 'Z' - 'A'
printf("%d\n",i);
2.4 逃逸字符
2.4.1 逃逸字符
用来表达无法打印出来的控制字符或特殊字符,由一个反斜杠\
开头,后面跟上另一个字符,两个字符和起来,组成了一个字符
printf("请分别输入身高的英尺和英寸"
"如输入\"5 7\"表示5英尺7英寸:");
3. 字符串
3.1 定义
- 以整数0结尾的一串字符 -> 0 <==> ‘\0’, 0!=‘0’;
- 0标志字符串的结束,但它不是字符串的一部分 -> 计算字符串长度的时候不包含这个 0;
- 字符串以数组的形式存在,以数组或指针的形式访问 -> 更多地是以指针的形式;
- <string.h>里有很多处理字符串的函数;
3.2 字符串变量
char *str = "Hello";
char word[] = "Hello";
char line[10] = "Hello";
3.3 字符串常量
char *s = "Hello World";
3.3.1
4. 字符串计算
4.1 常见错误
4.2 空字符串
4.3 string.h
- strlen:
size_t strlen(const char *s); //const 保证 strlen 不会修改字符串
#include<stdio.h>
#include<string.h>
int main(int argc, char const *argv[])
{
char line[] = "Hello";
printf("strlen=%lu\n",strlen(line));
printf("sizeof=%lu\n",sizeof(line));
return 0;
}
- strcmp:
int strcmp(const char *s1,const char *s2);
#include<stdio.h>
#include<string.h>
int main(int argc, char const *argv[])
{
char s1[] = "abc";
char s2[] = "abc";
printf("%d\n",strcmp(s1,s2));
return 0;
}
-
strcpy:
char *strcpy(char *restrict dst, const char *restrict src);
把src的字符串拷贝到dst,并返回dst -
strcat:
char *strcat(char *restrict s1, const char *restrict s2);
把s2拷贝到s1的后面,接成一个长的字符串,再返回s1,其中s1必须具有足够的空间
⚠️上述代码不够安全!(容易越界)
安全版本👇
char * strncpy(char *restrict dst, const char *restrict src,size_t n);
char * strncat(char *restrict s1, const char *restrict s2,size_t n);
int strncmp(const char *s1, const char *s2,size_t n);
char *strchr(const char *s, int c)
->从左到右查找c在字符串中第一次出现的位置,返回的是指针;
char *strrchr(const char *s, int c)
->从右到左查找c在字符串中第一次出现的位置,返回的是指针;
返回NUll
表示没有找到;