C语言:深入了解指针4(超级详细)
看之前必须得掌握有一定指针的知识,不然会看不懂,如果有不懂的可以看我博客 指针1,指针2,指针3 这三个讲了指针全部的基础知识超级详细,这篇只要是讲一些指针练习题也是非常详细
1. sizeof和strlen的对⽐
1. 基本定义和用途
sizeof:sizeof是一个运算符,不是函数。它用于计算数据类型或变量所占用的内存字节数。这个计算是在编译时完成的,与程序运行时的数据内容无关。strlen:strlen是一个标准库函数,定义在<string.h>(C)或<cstring>(C++)头文件中。它用于计算以'\0'结尾的字符串的实际长度,即字符串中字符的个数(不包括字符串结束符'\0'),这个计算是在程序运行时进行的。
2. 语法和使用示例
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello";
// 使用 sizeof 计算数组占用的内存字节数
size_t size = sizeof(str);
// 使用 strlen 计算字符串的实际长度
size_t length = strlen(str);
printf("sizeof(str) = %zu\n", size);
printf("strlen(str) = %zu\n", length);
return 0;
}
输出结果:
sizeof(str) = 6
strlen(str) = 5
在这个例子中,sizeof(str) 返回 6,因为数组 str 包含 5 个字符 'H'、'e'、'l'、'l'、'o' 以及一个字符串结束符 '\0',每个字符占 1 字节,所以总共 6 字节。而 strlen(str) 返回 5,因为它只计算字符串中实际的字符个数,不包括 '\0'。
3. 对不同数据类型的处理
sizeof- 对于基本数据类型,如
int、char、double等,sizeof返回该数据类型在当前系统中占用的字节数。例如,在 32 位系统中,sizeof(int)通常返回 4,sizeof(char)返回 1。 - 对于数组,
sizeof返回整个数组占用的内存字节数。 - 对于指针,
sizeof返回指针变量本身占用的内存字节数,而不是指针所指向的对象的大小。例如,在 32 位系统中,所有指针类型的sizeof结果通常都是 4 字节,在 64 位系统中通常是 8 字节。
- 对于基本数据类型,如
#include <stdio.h>
int main()
{
int arr[10];
int *ptr = arr;
printf("sizeof(arr) = %zu\n", sizeof(arr));
printf("sizeof(ptr) = %zu\n", sizeof(ptr));
return 0;
}
strlen:strlen只能用于以'\0'结尾的字符串。如果传递给strlen的参数不是一个有效的以'\0'结尾的字符串,会导致未定义行为,因为strlen会一直向后查找'\0',直到找到为止。
4. 性能差异
sizeof:由于sizeof是在编译时计算的,不会产生运行时开销,因此性能非常高。strlen:strlen需要在运行时遍历字符串,直到找到'\0'为止,因此其时间复杂度为 ,其中 是字符串的长度。当处理长字符串时,strlen的性能会受到一定影响。
5. 总结
- 如果需要知道数据类型或变量占用的内存大小,应该使用
sizeof。 - 如果需要知道以
'\0'结尾的字符串的实际长度,应该使用strlen。
2. 数组和指针笔试题解析
通过上面对strlen和sizeof的了解,结合我之前发的指针1和指针2,指针3的知识,我们来做一些综合性的题目。
2.1 ⼀维数组
认真思考哦
#include<stdio.h>
int main()
{int a[] = { 1,2,3,4 };// 1. sizeof(a)printf("%d\n", sizeof(a));// 2. sizeof(a + 0)printf("%d\n", sizeof(a + 0));// 3. sizeof(*a)printf("%d\n", sizeof(*a));// 4. sizeof(a + 1)printf("%d\n", sizeof(a + 1));// 5. sizeof(a[1])printf("%d\n", sizeof(a[1]));// 6. sizeof(&a)printf("%d\n", sizeof(&a));// 7. sizeof(*&a)printf("%d\n", sizeof(*&a));// 8. sizeof(&a + 1)printf("%d\n", sizeof(&a + 1));// 9. sizeof(&a[0])printf("%d\n", sizeof(&a[0]));// 10. sizeof(&a[0] + 1)printf("%d\n", sizeof(&a[0] + 1));return 0;
}
我们接下来来分析一下:
我们来回顾一下我之前提到过的:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),数组名单独放在括号里。
2. &数组名
1. sizeof(a)
a是一个包含 4 个int类型元素的数组。sizeof运算符作用于数组名时,返回整个数组占用的内存字节数。- 每个
int类型占 4 个字节,数组有 4 个元素,所以sizeof(a)的结果是4 * 4 = 16字节。
2. sizeof(a + 0)
- 根据数组名的特性,当数组名出现在表达式中(除了作为
sizeof或&运算符的操作数),它会隐式转换为指向数组首元素的指针(int *类型)。 a + 0等价于指向数组首元素的指针,指针在 32 位系统下占 4 个字节,在 64 位系统下占 8 个字节。所以sizeof(a + 0)的结果是指针的大小。
3. sizeof(*a)
a隐式转换为指向数组首元素的指针,*a表示解引用该指针,得到数组的首元素,即a[0]。a[0]是int类型,int类型占 4 个字节,所以sizeof(*a)的结果是 4 字节。
4. sizeof(a + 1)
a隐式转换为指向数组首元素的指针,a + 1是指向数组第二个元素的指针(int *类型)。- 指针的大小在 32 位系统下是 4 字节,在 64 位系统下是 8 字节,所以
sizeof(a + 1)的结果是指针的大小。
5. sizeof(a[1])
a[1]是数组的第二个元素,类型为int。int类型占 4 个字节,所以sizeof(a[1])的结果是 4 字节。
6. sizeof(&a)
&a是一个指向整个数组的指针(int (*) [4]类型),虽然它和指向数组首元素的指针在数值上可能相同,但类型不同。- 指针的大小在 32 位系统下是 4 字节,在 64 位系统下是 8 字节,所以
sizeof(&a)的结果是指针的大小。
7. sizeof(*&a)
&a是指向整个数组的指针,*&a等价于a,也就是整个数组。- 整个数组包含 4 个
int类型元素,每个int占 4 个字节,所以sizeof(*&a)的结果是4 * 4 = 16字节。
8. sizeof(&a + 1)
&a是指向整个数组的指针(int (*) [4]类型),&a + 1是指向下一个与a同类型数组的指针。- 指针的大小在 32 位系统下是 4 字节,在 64 位系统下是 8 字节,所以
sizeof(&a + 1)的结果是指针的大小。
9. sizeof(&a[0])
&a[0]是指向数组首元素的指针(int *类型)。- 指针的大小在 32 位系统下是 4 字节,在 64 位系统下是 8 字节,所以
sizeof(&a[0])的结果是指针的大小。
10. sizeof(&a[0] + 1)
&a[0]是指向数组首元素的指针(int *类型),&a[0] + 1是指向数组第二个元素的指针(int *类型)。- 指针的大小在 32 位系统下是 4 字节,在 64 位系统下是 8 字节,所以
sizeof(&a[0] + 1)的结果是指针的大小。
2.2 字符数组
接下来我们来几组字符数组来练习练习一下吧
代码1:
#include <stdio.h>int main()
{char arr[] = { 'a','b','c','d','e','f' };// 1. sizeof(arr)printf("%d\n", sizeof(arr));// 2. sizeof(arr + 0)printf("%d\n", sizeof(arr + 0));// 3. sizeof(*arr)printf("%d\n", sizeof(*arr));// 4. sizeof(arr[1])printf("%d\n", sizeof(arr[1]));// 5. sizeof(&arr)printf("%d\n", sizeof(&arr));// 6. sizeof(&arr + 1)printf("%d\n", sizeof(&arr + 1));// 7. sizeof(&arr[0] + 1)printf("%d\n", sizeof(&arr[0] + 1));return 0;
} 接下来我们来详细的分析一下吧:
我们来回顾一下我之前提到过的:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),数组名单独放在括号里。
2. &数组名
1. sizeof(arr)
arr是一个字符数组,当sizeof运算符作用于数组名时,它返回整个数组所占用的内存字节数。- 该数组包含 6 个
char类型的元素,每个char类型通常占用 1 个字节,所以sizeof(arr)的结果是6 * 1 = 6。
2. sizeof(arr + 0)
- 当数组名
arr出现在表达式中(除了作为sizeof或&运算符的操作数)时,它会隐式转换为指向数组首元素的指针(char*类型),即arr退化为char *类型的指针。 arr + 0仍然是指向数组首元素的指针,sizeof计算的是指针的大小。在 32 位系统中,指针大小通常为 4 字节;在 64 位系统中,指针大小通常为 8 字节。
3. sizeof(*arr)
- 由于
arr退化为指向数组首元素的指针,*arr表示对该指针进行解引用,得到数组的首元素arr[0]。 arr[0]是char类型,char类型占用 1 个字节,所以sizeof(*arr)的结果是 1。
4. sizeof(arr[1])
arr[1]是数组的第二个元素,其类型为char。- 因此,
sizeof(arr[1])的结果同样是 1 字节。
5. sizeof(&arr)
&arr是一个指向整个数组的指针,其类型为char (*)[6],表示指向包含 6 个char元素的数组的指针。- 但无论指针指向何种类型的对象,
sizeof计算的都是指针本身的大小。所以在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
6. sizeof(&arr + 1)
&arr是指向整个数组的指针(char (*)[6]),&arr + 1表示对该指针进行偏移,使其指向下一个与arr同类型的数组的起始位置。- 它仍然是一个指针,所以
sizeof(&arr + 1)的结果和sizeof(&arr)一样,在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
7. sizeof(&arr[0] + 1)
&arr[0]是数组首元素的地址,类型为char *。&arr[0] + 1是指向数组第二个元素的指针,同样是char *类型。- 所以
sizeof(&arr[0] + 1)计算的是指针的大小,在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
代码2:
#include <stdio.h>
#include <string.h>int main()
{char arr[] = { 'a','b','c','d','e','f' };// 1. strlen(arr)printf("%d\n", strlen(arr));// 2. strlen(arr + 0)printf("%d\n", strlen(arr + 0));// 3. strlen(*arr)printf("%d\n", strlen(*arr));// 4. strlen(arr[1])printf("%d\n", strlen(arr[1]));// 5. strlen(&arr)printf("%d\n", strlen(&arr));// 6. strlen(&arr + 1)printf("%d\n", strlen(&arr + 1));// 7. strlen(&arr[0] + 1)printf("%d\n", strlen(&arr[0] + 1));return 0;
}
接下来我们来详细的分析一下吧:
我们来回顾一下我之前提到过的:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),数组名单独放在括号里。
2. &数组名
1. strlen(arr)
arr是一个字符数组,但是该数组初始化时没有包含字符串结束符'\0'。strlen函数会从arr指向(char*类型)的地址开始,逐个字符向后查找'\0',直到找到为止。由于arr中没有'\0',strlen会继续访问数组后面的内存,这会导致未定义行为,其输出结果是不确定的,可能是一个随机值,甚至可能引发程序崩溃。
2. strlen(arr + 0)
arr + 0等同于arr,因为数组名arr在表达式中(除了作为sizeof或&运算符的操作数)会隐式转换为指向数组首元素的指针(char*类型),加上 0 后仍然指向数组首元素。- 同样,由于数组中没有
'\0',这也会导致未定义行为,输出结果是一个随机值。
3. strlen(*arr)
*arr对arr进行解引用,得到数组的首元素'a',其 ASCII 值为 97。strlen函数期望的参数是一个指向以'\0'结尾的字符串的指针(char *类型),而这里传递的是一个char类型的值(97)。将这个值当作指针来处理是错误的,会导致未定义行为,97作为地址传给给了strlen,可能会引发程序崩溃。
4. strlen(arr[1])
arr[1]是数组的第二个元素'b',其 ASCII 值为 98。- 与
strlen(*arr)类似,strlen函数期望的是一个指针,而这里传递的是一个char类型的值,会导致未定义行为,98作为地址传给给了strlen,可能会引发程序崩溃。
5. strlen(&arr)
&arr是一个指向整个数组的指针,其类型为char (*)[6]。strlen函数期望的参数是char *类型的指针,这里传递的指针类型不匹配。将char (*)[6]类型的指针当作char *类型的指针来使用会导致未定义行为,可能会引发程序崩溃,也可能是一个随机值。
6. strlen(&arr + 1)
&arr是指向整个数组的指针char (*)[6],&arr + 1会跳过整个数组,指向下一个与arr同类型的数组的起始位置的地址。- 同样,
strlen函数期望的是char *类型的指针,这里指针类型不匹配,并且该位置的内存内容是不确定的,会导致未定义行为,可能会引发程序崩溃,也可能是一个随机值。
7. strlen(&arr[0] + 1)
&arr[0]是数组首元素的地址,&arr[0] + 1是指向数组第二个元素的指针(char*类型)。- 由于数组中没有
'\0',strlen会从第二个元素开始向后查找'\0',但一直找不到,会继续访问数组后面的内存,这会导致未定义行为,输出结果不确定,也可能是一个随机值。
代码3
#include <stdio.h>int main()
{char arr[] = "abcdef";// 1. sizeof(arr)printf("%d\n", sizeof(arr));// 2. sizeof(arr + 0)printf("%d\n", sizeof(arr + 0));// 3. sizeof(*arr)printf("%d\n", sizeof(*arr));// 4. sizeof(arr[1])printf("%d\n", sizeof(arr[1]));// 5. sizeof(&arr)printf("%d\n", sizeof(&arr));// 6. sizeof(&arr + 1)printf("%d\n", sizeof(&arr + 1));// 7. sizeof(&arr[0] + 1)printf("%d\n", sizeof(&arr[0] + 1));return 0;
}
接下来我们来详细的分析一下吧:
我们来回顾一下我之前提到过的:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),数组名单独放在括号里。
2. &数组名
1. sizeof(arr)
arr是一个字符数组,使用字符串字面量"abcdef"进行初始化。字符串字面量在 C 语言中会自动在末尾添加一个字符串结束符'\0'。- 所以数组
arr实际上包含了 7 个字符:'a','b','c','d','e','f','\0'。 sizeof运算符作用于数组名时,返回整个数组所占用的内存字节数。每个char类型占 1 个字节,因此sizeof(arr)的结果是 7。
2. sizeof(arr + 0)
- 当数组名
arr出现在表达式中(除了作为sizeof或&运算符的操作数)时,它会隐式转换为指向数组首元素的指针,即arr退化为char *类型的指针。 arr + 0仍然指向数组的首元素,本质上还是一个指针。sizeof计算的是指针的大小。在 32 位系统中,指针大小通常为 4 字节;在 64 位系统中,指针大小通常为 8 字节。
3. sizeof(*arr)
- 由于
arr退化为指向数组首元素的指针,*arr表示对该指针进行解引用,得到数组的首元素arr[0],也就是字符'a'。 arr[0]是char类型,char类型占用 1 个字节,所以sizeof(*arr)的结果是 1。
4. sizeof(arr[1])
arr[1]是数组的第二个元素,其类型为char。- 因此,
sizeof(arr[1])的结果同样是 1 字节。
5. sizeof(&arr)
&arr是一个指向整个数组的指针,其类型为char (*)[7],表示指向包含 7 个char元素的数组的指针。- 但无论指针指向何种类型的对象,
sizeof计算的都是指针本身的大小。所以在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
6. sizeof(&arr + 1)
&arr是指向整个数组的指针(char (*)[7]),&arr + 1表示对该指针进行偏移,使其指向下一个与arr同类型的数组的起始位置。- 它仍然是一个指针,所以
sizeof(&arr + 1)的结果和sizeof(&arr)一样,在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
7. sizeof(&arr[0] + 1)
&arr[0]是数组首元素的地址,类型为char *。&arr[0] + 1是指向数组第二个元素的指针,同样是char *类型。- 所以
sizeof(&arr[0] + 1)计算的是指针的大小,在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
代码4
#include <stdio.h>
#include <string.h>int main()
{// 定义并初始化字符数组 arrchar arr[] = "abcdef";// 1. strlen(arr)printf("%d\n", strlen(arr));// 2. strlen(arr + 0)printf("%d\n", strlen(arr + 0));// 3. strlen(*arr)printf("%d\n", strlen(*arr));// 4. strlen(arr[1])printf("%d\n", strlen(arr[1]));// 5. strlen(&arr)printf("%d\n", strlen(&arr));// 6. strlen(&arr + 1)printf("%d\n", strlen(&arr + 1));// 7. strlen(&arr[0] + 1)printf("%d\n", strlen(&arr[0] + 1));return 0;
}
接下来我们来详细的分析一下吧:
我们来回顾一下我之前提到过的:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),数组名单独放在括号里。
2. &数组名
1 printf(strlen(arr));
arr作为数组名,在strlen函数调用时,它会隐式转换为指向数组首元素的指针(这是数组在表达式中的一种退化行为),也就是指向字符'a'的指针(char*)。strlen函数会从该指针指向的位置开始,逐个字符向后查找,直到遇到'\0'为止。从'a'开始,依次是'b'、'c'、'd'、'e'、'f',再到'\0',总共 6 个有效字符(不包含'\0')。所以strlen(arr)的返回值是 6,程序会输出6。
2 printf(strlen(arr + 0));
arr + 0本质上和arr是一样的,因为指针(char*)加上 0 并不会改变其指向。所以arr + 0依然指向数组的首元素'a'。- 因此,
strlen(arr + 0)的计算过程和strlen(arr)相同,返回值也是 6,程序会输出6。
3 printf(strlen(*arr));
*arr是对arr这个指针进行解引用操作,得到的是数组的首元素'a'。'a'的 ASCII 码值是 97。- 但
strlen函数期望的参数是一个指向以'\0'结尾的字符串的指针(char *类型)。这里把一个char类型的值(97)当作指针(地址)传递给strlen,这会导致未定义行为。程序可能会尝试从内存地址 97 开始查找'\0',这可能会引发段错误(因为程序可能没有访问该内存地址的权限),程序崩溃或者输出一个无意义的结果。
4 printf(strlen(arr[1]));
arr[1]表示数组的第二个元素,即字符'b',其 ASCII 码值为 98。- 同样,
strlen函数需要的是指针(char*)类型的指针参数,这里传递的是char类型的值,98作为地址传给给了strlen会导致未定义行为,程序可能崩溃或者给出错误的输出。
5 printf(strlen(&arr));
&arr是一个指向整个数组的指针,其类型为char (*)[7](因为数组arr有 7 个元素)。- 而
strlen函数要求的参数是char *类型的指针。将char (*)[7]类型的指针当作char *类型的指针使用是错误的但也可以算出是6,也会导致未定义行为,程序可能无法正常运行。
6 printf(strlen(&arr + 1));
&arr指向整个数组(char (*)[7]),&arr + 1会使指针跳过整个数组arr,指向下一个和arr同类型数组应该存放的起始位置。- 这个位置的内存内容是不确定的,而且传递的指针类型与
strlen函数期望的char *类型不匹配,会引发未定义行为,程序可能出现异常。
7 printf(strlen(&arr[0] + 1));
&arr[0]是数组首元素的地址,&arr[0] + 1是指向数组第二个元素'b'的指针(char*)。strlen函数从这个指针指向的位置开始查找'\0'。从'b'开始,依次是'c'、'd'、'e'、'f',再到'\0',一共有 5 个有效字符。所以strlen(&arr[0] + 1)的返回值是 5,程序会输出5。
代码5
#include <stdio.h>int main()
{char* p = "abcdef";// 1. sizeof(p)printf("%d\n", sizeof(p));// 2. sizeof(p + 1)printf("%d\n", sizeof(p + 1));// 3. sizeof(*p)printf("%d\n", sizeof(*p));// 4. sizeof(p[0])printf("%d\n", sizeof(p[0]));// 5. sizeof(&p)printf("%d\n", sizeof(&p));// 6. sizeof(&p + 1)printf("%d\n", sizeof(&p + 1));// 7. sizeof(&p[0] + 1)printf("%d\n", sizeof(&p[0] + 1));return 0;
}
接下来我们来详细的分析一下吧:
怕大家搞混这边补充一些指针知识:
1.
p的类型及含义在代码
char* p = "abcdef";中,p是一个字符指针,其类型为char *。它指向字符串字面量"abcdef"的首字符'a'的地址。2.
p[0]的含义在 C 语言里,对于指针
p,p[i]等价于*(p + i)。当i为 0 时,p[0]等价于*(p + 0),也就是*p。所以p[0]表示的是指针p所指向的字符,在这里就是字符'a',其类型为char。3.
&p[0]的类型推导
&是取地址运算符,&p[0]表示取p[0]的地址。由于p[0]是char类型的字符'a',那么&p[0]就是指向这个char类型字符的指针,其类型为char *。4.&p的含义
&是取地址运算符,当它作用于指针变量p时,&p得到的是指针变量p自身在内存中的存储地址。由于p本身是char *类型(指向char的指针),那么指向p的指针就需要能够存储p的地址,这个指针指向的对象类型是char *,所以&p的类型是指向char *类型的指针,即char **。从本质上来说,
&p[0]和p指向的是同一个地址,即字符串"abcdef"的首字符地址,只是它们在概念上稍有不同:p是一个指针变量,而&p[0]是通过对数组(这里可以把指针p当作指向字符串数组首元素的指针)首元素取地址得到的指针。综上所述,p是char *类型,p[0]是char类型,&p[0]是char *类型,&p是char **类型。。
1 sizeof(p)
p是一个(char *类型)指针变量(不是数组名),它存储的是字符串"abcdef"的首地址('a')。sizeof操作符用于计算指针变量本身所占用的内存大小。在 32 位系统中,指针通常占用 4 个字节;在 64 位系统中,指针通常占用 8 个字节。所以sizeof(p)的结果是 4(32 位系统)或 8(64 位系统)。
2 sizeof(p + 1)
p + 1是一个指针运算,它会让指针p向后偏移一个char类型的大小,从而指向字符串中的第二个字符'b'。- 但无论指针指向何处,
sizeof计算的都是指针本身的大小。所以sizeof(p + 1)的结果和sizeof(p)一样,是 4(32 位系统)或 8(64 位系统)。
3 sizeof(*p)
*p是对指针p进行解引用操作,得到的是指针p所指向的字符,也就是字符串的首字符'a'。'a'是char类型,char类型在 C 语言中通常占用 1 个字节。所以sizeof(*p)的结果是 1。
4 sizeof(p[0])
p[0](char类型)等价于*(p + 0),也就是指针p所指向的字符,同样是'a'。- 因此,
sizeof(p[0])的结果和sizeof(*p)相同,为 1。
5 sizeof(&p)
&p是取指针变量p本身的地址,它是一个指向指针的指针(类型为char **)。- 不管指针指向的是什么类型,
sizeof计算的都是指针本身的大小。所以sizeof(&p)的结果是 4(32 位系统)或 8(64 位系统)。
6 sizeof(&p + 1)
&p是指向指针p的指针,&p + 1会让这个指针向后偏移一个char **类型的大小,指向下一个char **类型的位置。- 但
sizeof计算的依然是指针本身的大小,所以sizeof(&p + 1)的结果和sizeof(&p)一样,是 4(32 位系统)或 8(64 位系统)。
7 sizeof(&p[0] + 1)
p[0](char类型) 是字符串的首字符'a',&p[0]是首字符'a'的地址,也就是指针p所指向的地址。&p[0] + 1会让这个指针向后偏移一个char类型的大小,指向字符串中的第二个字符'b'。- 它仍然是一个指针(
&p[0]char *类型),所以sizeof(&p[0] + 1)的结果和sizeof(p)相同,是 4(32 位系统)或 8(64 位系统)。
代码6
#include<stdio.h>
#include<string.h>
int main()
{char* p = "abcdef";//1.strlen(p)printf("%d\n", strlen(p));//2.strlen(p + 1)printf("%d\n", strlen(p + 1));//3.strlen(*p)printf("%d\n", strlen(*p));//4.strlen(p[0])printf("%d\n", strlen(p[0]));//strlen(&p)printf("%d\n", strlen(&p));//6.strlen(&p + 1)printf("%d\n", strlen(&p + 1));//7.strlen(&p[0] + 1)printf("%d\n", strlen(&p[0] + 1));return 0;
}
接下来我们来详细的分析一下吧:
-
strlen(p)p(char*类型)是一个指向字符串常量"abcdef"的指针,strlen(p)会从p所指向的字符开始,逐个字符计数,直到遇到字符串结束符'\0'为止。- 字符串
"abcdef"的长度是 6,所以输出结果为6。
-
strlen(p + 1)p + 1(char*类型)指向字符串"abcdef"的第二个字符'b'。strlen(p + 1)会从'b'开始计数,直到遇到字符串结束符'\0',所以输出结果为5。
-
strlen(*p)*p(char类型)是解引用操作,它得到的是p所指向的第一个字符'a',其 ASCII 码值为 97。strlen函数的参数应该是一个指向字符串的指针,而这里传递的是一个字符(实际上是一个整数),这会导致未定义行为,程序可能会崩溃或输出错误的结果。
-
strlen(p[0])p[0](char类型)等价于*p,同样得到的是字符'a'。- 传递字符给
strlen函数会导致未定义行为,程序可能会崩溃或输出错误的结果。
-
strlen(&p)&p(char**类型)是指针p的地址,它指向的并不是一个以'\0'结尾的字符串。- 传递指针
p的地址给strlen函数会导致未定义行为,程序可能会崩溃或随机值的结果。
-
strlen(&p + 1)&p + 1(char**类型)是指针p的地址向后偏移一个char*类型的大小。- 它指向的同样不是一个以
'\0'结尾的字符串,传递该地址给strlen函数会导致未定义行为,程序可能会崩溃或随机值的结果。
-
strlen(&p[0] + 1)&p[0](char*类型)等价于p,指向字符串"abcdef"的第一个字符'a'。&p[0] + 1指向字符串的第二个字符'b'。strlen(&p[0] + 1)会从'b'开始计数,直到遇到字符串结束符'\0',所以输出结果为5。
2.3 ⼆维数组
#include<stdio.h>int main()
{// 定义一个 3 行 4 列的二维数组 a,并初始化为全 0int a[3][4] = { 0 };// 1.sizeof(a)printf("%d\n", sizeof(a));// 2.sizeof(a[0][0])printf("%d\n", sizeof(a[0][0]));// 3.sizeof(a[0])printf("%d\n", sizeof(a[0]));// 4.sizeof(a[0] + 1)printf("%d\n", sizeof(a[0] + 1));// 5.sizeof(*(a[0] + 1))printf("%d\n", sizeof(*(a[0] + 1)));// 6.sizeof(a + 1)printf("%d\n", sizeof(a + 1));// 7.sizeof(*(a + 1))printf("%d\n", sizeof(*(a + 1)));// 8.sizeof(&a[0] + 1)printf("%d\n", sizeof(&a[0] + 1));// 9.sizeof(*(&a[0] + 1))printf("%d\n", sizeof(*(&a[0] + 1)));// 10.sizeof(*a)printf("%d\n", sizeof(*a));// 11.sizeof(a[3])printf("%d\n", sizeof(a[3]));return 0;
}
接下来我们来详细的分析一下吧:
-
sizeof(a)a是一个3行4列的二维数组,每个元素是int类型。- 在大多数系统中,
int类型占4个字节,所以整个数组的元素个数为3 * 4 = 12个。 - 因此,
sizeof(a)的结果是3 * 4 * sizeof(int) = 48字节。
-
sizeof(a[0][0])a[0][0]表示数组a的第一个元素,它是int类型。- 通常
int类型占4个字节,所以sizeof(a[0][0])的结果是4字节。
-
sizeof(a[0])a[0](int *类型) 表示数组a的第一行,它可以看作是一个包含4个int元素的一维数组。- 所以
sizeof(a[0])的结果是4 * sizeof(int) = 16字节。
-
sizeof(a[0] + 1)a[0]是第一行数组的首地址,a[0] + 1是第一行第二个元素的地址。- 地址本质上是一个指针,在大多数系统中,指针的大小是固定的,通常为
4或8字节(取决于系统是 32 位还是 64 位)。 - 所以
sizeof(a[0] + 1)的结果是指针的大小,一般为4或8字节。
-
sizeof(*(a[0] + 1))*(a[0] + 1)表示对第一行第二个元素的地址进行解引用,得到的是第一行第二个元素。- 该元素是
int类型,所以sizeof(*(a[0] + 1))的结果是4字节。
-
sizeof(a + 1)a(int (*)[4]类型)是二维数组名,a + 1表示跳过第一行,指向第二行的首地址。- 它是一个指针,所以
sizeof(a + 1)的结果是指针的大小,一般为4或8字节。
-
sizeof(*(a + 1))*(a + 1)表示对指向第二行的指针进行解引用,得到的是第二行数组。- 第二行数组包含
4个int元素,所以sizeof(*(a + 1))的结果是4 * sizeof(int) = 16字节。
-
sizeof(&a[0] + 1)&a[0](int (*)[4]类型) 是第一行数组的地址,&a[0] + 1表示跳过第一行,指向第二行的地址。- 它是一个指针,所以
sizeof(&a[0] + 1)的结果是指针的大小,一般为4或8字节。
-
sizeof(*(&a[0] + 1))*(&a[0] + 1)表示对指向第二行的地址进行解引用,得到的是第二行数组。- 所以
sizeof(*(&a[0] + 1))的结果是4 * sizeof(int) = 16字节。
-
sizeof(*a)*a(int *类型) 等价于a[0],表示第一行数组。- 所以
sizeof(*a)的结果是4 * sizeof(int) = 16字节。
-
sizeof(a[3])- 虽然数组
a只有3行(索引从0到2),a[3]属于越界访问。 - 但
sizeof是在编译时计算大小,它不会真正访问数组元素,a[3]被看作是一个包含4个int元素的一维数组。 - 所以
sizeof(a[3])的结果是4 * sizeof(int) = 16字节。
- 虽然数组
3. 指针运算笔试题解析
3.1 题⽬1:
#include <stdio.h>
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int *ptr = (int *)(&a + 1);printf( "%d,%d", *(a + 1), *(ptr - 1));return 0;
} 详细讲解:
&a:这里的&a表示整个数组a的地址,它的类型是int (*)[5],即指向包含 5 个整型元素的数组的指针。&a + 1:由于&a是指向整个数组的指针,&a + 1会跳过整个数组的内存空间。在这个例子中,数组a包含 5 个整型元素,每个整型元素通常占 4 个字节(取决于系统),所以&a + 1会跳过5 * 4 = 20个字节,指向数组a之后的内存地址。(int *):这是一个强制类型转换操作,将&a + 1的类型从int (*)[5]转换为int *,这样ptr就变成了一个指向整型的指针。
printf 函数输出
*(a + 1):数组名a代表数组首元素的地址,a + 1表示数组首元素地址向后偏移一个整型元素的位置,即指向数组的第二个元素。*(a + 1)是对该地址进行解引用操作,得到该地址存储的值,也就是数组的第二个元素 2。*(ptr - 1):ptr指向数组a之后的内存地址,ptr - 1表示将指针ptr向前偏移一个整型元素的位置,即指向数组a的最后一个元素。*(ptr - 1)是对该地址进行解引用操作,得到该地址存储的值,也就是数组的最后一个元素 5。
3.2 题⽬2
/在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
#include<stdio.h>
struct Test
{int Num;char *pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
} 详细讲解:
- 头文件:
#include <stdio.h>引入标准输入输出库,以便后续使用printf函数进行输出。 - 结构体定义:定义了
struct Test结构体,它包含了不同类型的成员变量。 - 指针初始化:声明了一个指向
struct Test类型的指针p,并将其初始化为地址0x100000。这里通过强制类型转换(struct Test*)把0x100000转换为struct Test*类型。
1 printf("%p\n", p + 0x1);
- 指针运算规则:在 C 语言里,当对指针进行加减运算时,偏移量是根据指针所指向的数据类型的大小来计算的。
p是指向struct Test类型的指针,已知struct Test结构体大小为 20 字节。 - 具体计算:
p + 0x1表示将指针p向后偏移 1 个struct Test结构体的大小,也就是偏移 20 字节。因为十六进制中,20 字节对应的十六进制数是0x14(十进制 20 转换为十六进制为0x14)。所以p + 0x1得到的地址是0x100000 + 0x14 = 0x100014。 - 输出结果:这行代码会输出
0x100014。
2 printf("%p\n", (unsigned long)p + 0x1);
- 类型转换与运算:首先将指针
p强制转换为unsigned long类型,此时p就变成了一个无符号长整型数值。在进行(unsigned long)p + 0x1运算时,只是简单地对这个无符号长整型数值进行加法操作,即把地址值0x100000加上1(整数加1)。 - 输出结果:所以这行代码会输出
0x100001。
3 printf("%p\n", (unsigned int*)p + 0x1);
- 类型转换与指针运算:先把指针
p强制转换为unsigned int*类型,也就是指向无符号整型的指针。在 X86 环境下,无符号整型unsigned int通常占用 4 个字节。当对unsigned int*类型的指针进行加法运算时,偏移量是根据无符号整型的大小来计算的。 - 具体计算:
(unsigned int*)p + 0x1表示将指针向后偏移 1 个无符号整型的大小,即偏移 4 个字节。所以得到的地址是0x100000 + 0x4 = 0x100004。 - 输出结果:这行代码会输出
0x100004。
3.3 题⽬3
#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int *p;p = a[0];printf( "%d", p[0]);return 0;
} 详细讲解:
- 这里定义了一个 3 行 2 列的二维数组
a。但需要注意的是,初始化列表中使用的是逗号表达式(0, 1)、(2, 3)和(4, 5)。 - 在 C 语言里,逗号表达式会从左到右依次计算每个表达式的值,并且整个逗号表达式的值是最后一个表达式的值。所以
(0, 1)的值是 1,(2, 3)的值是 3,(4, 5)的值是 5。 - 因此,数组
a实际的初始化情况是:
a[0][0] = 1;
a[0][1] = 3;
a[1][0] = 5;
a[1][1] = 未初始化的值(默认是随机值);
a[2][0] = 未初始化的值(默认是随机值);
a[2][1] = 未初始化的值(默认是随机值);
- 定义了一个整型指针
p。 a[0]是二维数组a第一行的首地址,它的类型是int *。将a[0]赋值给p后,p就指向了数组a的第一行的第一个元素。p[0]等价于*(p + 0),也就是p所指向的元素的值。由于p指向数组a的第一行的第一个元素,而该元素的值是 1,所以这行代码会输出 1。
3.4 题⽬4
//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
int main()
{int a[5][5];int(*p)[4];p = a;printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;
} 
详细讲解:
- 计算
&p[4][2]的地址:p是指向包含 4 个整型元素的数组的指针,p[4]表示p向后移动 4 行,由于每行有 4 个整型元素,所以p向后移动了4 * 4 = 16个整型元素的位置。p[4][2]表示在p[4]这一行的基础上再向后移动 2 个整型元素的位置。因此,&p[4][2]相对于p所指向的起始地址偏移了16 + 2 = 18个整型元素的位置。
- 计算
&a[4][2]的地址:a是一个 5 行 5 列的二维数组,a[4]表示第 5 行(数组下标从 0 开始)的首地址,相对于a的起始地址向后移动了4 * 5 = 20个整型元素的位置。a[4][2]表示在第 5 行的基础上再向后移动 2 个整型元素的位置。所以,&a[4][2]相对于a的起始地址偏移了20 + 2 = 22个整型元素的位置。
- 计算地址差值:
&p[4][2] - &a[4][2]表示两个地址之间相差的整型元素个数。这里&p[4][2]相对于起始地址偏移了 18 个整型元素,&a[4][2]相对于起始地址偏移了 22 个整型元素,所以它们的差值为18 - 22 = -4。
- 输出结果:
%p是用于输出指针地址的格式说明符,但在输出指针相减的结果时,它会将差值以十六进制的形式输出。由于差值是 -4,在计算机中以补码形式存储,%p不会对存储的二进制转换,所以存储的是什么就输出什么,对于 32 位系统,-4 的补码是0xFFFFFFFC(没有转换为原码,所以是一个非常大的数)。%d是用于输出十进制整数的格式说明符,会直接输出 -4。
3.5 题⽬5
#include <stdio.h>
int main()
{int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int *ptr1 = (int *)(&aa + 1);int *ptr2 = (int *)(*(aa + 1));printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));return 0;
}

详细讲解:
指针 ptr1 的定义与初始化
&aa:这里的&aa表示整个二维数组aa的地址,其类型是int (*)[2][5],即指向一个包含2行5列整型元素数组的指针。&aa + 1:因为&aa是指向整个二维数组的指针,&aa + 1会跳过整个数组aa所占用的内存空间。在这个例子中,数组aa包含2 * 5 = 10个整型元素,每个整型元素通常占4个字节(取决于系统),所以&aa + 1会指向数组aa之后的内存地址。(int *):这是一个强制类型转换操作,将&aa + 1的类型从int (*)[2][5]转换为int *,这样ptr1就变成了一个指向整型的指针,它指向数组aa之后的第一个整型位置。
指针 ptr2 的定义与初始化
aa:数组名aa在大多数表达式中会隐式转换为指向其首元素的指针,对于二维数组aa,它会转换为指向第一行的指针,类型为int (*)[5]。aa + 1:由于aa是指向第一行的指针,aa + 1会指向数组的第二行,其类型仍然是int (*)[5]。*(aa + 1):对aa + 1进行解引用操作,得到第二行数组的首地址,其类型是int *,也就是指向第二行第一个元素的指针。- 这里的强制类型转换
(int *)实际上是多余的,因为*(aa + 1)本身就是int *类型。ptr2最终指向数组aa第二行的第一个元素。
printf 函数输出
*(ptr1 - 1):ptr1指向数组aa之后的内存地址,ptr1 - 1表示将指针ptr1向前偏移一个整型元素的位置,即指向数组aa的最后一个元素。*(ptr1 - 1)是对该地址进行解引用操作,得到该地址存储的值,也就是10。*(ptr2 - 1):ptr2指向数组aa第二行的第一个元素,ptr2 - 1表示将指针ptr2向前偏移一个整型元素的位置,即指向数组aa第一行的最后一个元素。*(ptr2 - 1)是对该地址进行解引用操作,得到该地址存储的值,也就是5。
3.6 题⽬6
#include <stdio.h>
int main()
{char *a[] = {"work","at","alibaba"};char**pa = a;pa++;printf("%s\n", *pa);return 0;
} 
详细讲解:
- 这里定义了一个指针数组
a。指针数组是指数组的每个元素都是指针类型。在这个例子中,数组a的元素是char *类型,也就是字符指针。 - 初始化列表中的
"work"、"at"、"alibaba"是字符串常量。在 C 语言里,字符串常量会被存储在只读内存区域,并且在使用时会隐式转换为指向其首字符的指针。所以,a[0]指向字符串"work"的首字符'w',a[1]指向字符串"at"的首字符'a',a[2]指向字符串"alibaba"的首字符'a'。 - 定义了一个二级指针
pa,二级指针就是指向指针的指针。 - 数组名
a在大多数表达式中会隐式转换为指向其首元素的指针。由于a的元素是char *类型,所以a会转换为char **类型的指针,指向a[0]。因此,这里将a赋值给pa后,pa就指向了指针数组a的第一个元素a[0]。 - 因为
pa是char **类型的指针,对其进行++操作会让它指向下一个char *类型的元素。也就是说,pa原本指向a[0],执行pa++后,pa指向了a[1]。 *pa是对pa进行解引用操作,由于pa指向a[1],所以*pa就相当于a[1],而a[1]是指向字符串"at"首字符的指针。%s是printf函数用于输出字符串的格式说明符,它会从给定的指针所指向的字符开始,依次输出字符,直到遇到字符串结束符'\0'。所以,这里会输出字符串"at"。
3.7 题⽬7
#include <stdio.h>
int main()
{char *c[] = {"ENTER","NEW","POINT","FIRST"};char**cp[] = {c+3,c+2,c+1,c};char***cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *--*++cpp+3);printf("%s\n", *cpp[-2]+3);printf("%s\n", cpp[-1][-1]+1);return 0;
}

详细讲解:
字符指针数组 c 的定义与初始化
- 定义了一个字符指针数组
c,数组中的每个元素都是一个char *类型的指针,分别指向字符串常量"ENTER"、"NEW"、"POINT"和"FIRST"的首字符。
二级字符指针数组 cp 的定义与初始化
- 定义了一个二级字符指针数组
cp,数组中的元素是char **类型的指针。 c+3指向c数组的第 4 个元素(下标从 0 开始),即指向"FIRST"的指针;c+2指向c数组的第 3 个元素,即指向"POINT"的指针;c+1指向c数组的第 2 个元素,即指向"NEW"的指针;c指向c数组的第 1 个元素,即指向"ENTER"的指针。
三级字符指针 cpp 的定义与初始化
- 定义了一个三级字符指针
cpp,并将其初始化为指向二级字符指针数组cp的首元素。
第一个 printf 语句
++cpp:先将cpp指针向后移动一个位置,使其指向cp数组的第 2 个元素(原本指向cp[0],现在指向cp[1])。*++cpp:对移动后的cpp进行解引用,得到cp[1],即c+2,它指向c数组中"POINT"的指针。**++cpp:再对*++cpp进行解引用,得到"POINT"字符串的首地址。- 最终输出字符串
"POINT"。
第二个 printf 语句
++cpp:再次将cpp指针向后移动一个位置,使其指向cp数组的第 3 个元素(即cp[2])。*++cpp:对移动后的cpp进行解引用,得到cp[2],即c+1,它指向c数组中"NEW"的指针。--*++cpp:将*++cpp指向的指针向前移动一个位置,即指向c数组的第 1 个元素(c[0]),也就是"ENTER"的指针。*--*++cpp:对--*++cpp进行解引用,得到"ENTER"字符串的首地址。*--*++cpp+3:将"ENTER"字符串的首地址向后移动 3 个位置,指向字符'E'后面的第 3 个字符'E'。- 最终输出从该位置开始的字符串
"ER"。
第三个 printf 语句
cpp[-2]:等价于*(cpp - 2),将cpp指针向前移动 2 个位置,指向cp数组的第 1 个元素(cp[0]),即c+3,它指向c数组中"FIRST"的指针。*cpp[-2]:对cpp[-2]进行解引用,得到"FIRST"字符串的首地址。*cpp[-2]+3:将"FIRST"字符串的首地址向后移动 3 个位置,指向字符'R'。- 最终输出从该位置开始的字符串
"ST"。
第四个 printf 语句
cpp[-1]:等价于*(cpp - 1),由于cpp指向cp数组的第 3 个元素,所以cpp - 1指向cp数组的第 2 个元素(即c + 2)。cpp[-1][-1]:等价于*(*(cpp - 1) - 1),*(cpp - 1)得到c + 2,*(cpp - 1) - 1得到c + 1,再解引用得到指向"NEW"的指针。cpp[-1][-1] + 1:将指向"NEW"的指针向后移动 1 个位置,指向字符'E',从这个位置开始输出字符串,所以输出"EW"。
相关文章:
C语言:深入了解指针4(超级详细)
看之前必须得掌握有一定指针的知识,不然会看不懂,如果有不懂的可以看我博客 指针1,指针2,指针3 这三个讲了指针全部的基础知识超级详细,这篇只要是讲一些指针练习题也是非常详细 1. sizeof和strlen的对⽐ 1. 基本定义…...
C#+Redis接收数据并定时3秒钟频率异步保存到数据库
要在C#中实现从Redis接收数据,并以每3秒的频率异步保存到数据库,你可以使用System.Threading.Tasks.Task.Delay或System.Timers.Timer来创建一个定时任务。不过,对于更复杂的定时和调度需求,System.Threading.Tasks.Timer或Quartz.NET等库可能更合适。 在这个场景中,由于…...
CEF132 编译指南 Windows 篇 - 拉取 CEF 源码 (五)
1. 引言 获取 CEF 132 源码是开始编译工作的前提和关键步骤。在完成 depot_tools 的安装和配置后,我们需要通过正确的方式下载和同步 CEF 的源代码。由于 CEF 项目依赖于 Chromium 的大量组件,因此源码的获取过程需要特别注意同步策略和版本管理&#x…...
【鸿蒙开发】第二十四章 AI - Core Speech Kit(基础语音服务)
目录 1 简介 1.1 场景介绍 1.2 约束与限制 2 文本转语音 2.1 场景介绍 2.2 约束与限制 2.3 开发步骤 2.4 设置播报策略 2.4.1 设置单词播报方式 2.4.2 设置数字播报策略 2.4.3 插入静音停顿 2.4.4 指定汉字发音 2.5 开发实例 3 语音识别 3.1 场景介绍 3.2 约束…...
DeepSeek与llama本地部署(含WebUI)
DeepSeek从2025年1月起开始火爆,成为全球最炙手可热的大模型,各大媒体争相报道。我们可以和文心一言一样去官网进行DeepSeek的使用,那如果有读者希望将大模型部署在本地应该怎么做呢?本篇文章将会教你如何在本地傻瓜式的部署我们的…...
让万物「听说」:AI 对话式智能硬件方案和发展洞察
本文整理自声网 SDK 新业务探索组技术负责人,IoT 行业专家 吴方方 1 月 18 日在 RTE 开发者社区「Voice Agent 硬件分享会」上的分享。本次主要介绍了 AI 对话式智能硬件的发展历程,新一波 AI 浪潮所带来的创新机遇、技术挑战以及未来的展望。 在语音交…...
Day38-【13003】短文,二叉树,完全二叉树,二叉树的顺序存储,和链式存储
文章目录 第二节 二叉树二叉树的定义及重要性质n个结点,能组合成多少个不同的二叉树满二叉树、完全二叉树完全二叉树的性质二叉树的性质二叉树的结点数完全二叉树的高度 二叉树的存储顺序存储方式链式存储方式二叉链表的程序实现二叉链表空指针域计算 第二节 二叉树…...
MyBatis常见知识点
#{} 和 ${} 的区别是什么? 答: ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于原样文本替换,可以替换任意内容,比如${driver}会被原样替换为com.mysql.jdbc. Driver。 一个…...
4. 【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--什么是微服务--微服务设计原则与最佳实践
相比传统的单体应用,微服务架构通过将大型系统拆分成多个独立的小服务,不仅提升了系统的灵活性和扩展性,也带来了许多设计和运维上的挑战。如何在设计和实现微服务的过程中遵循一系列原则和最佳实践,从而构建一个稳定、高效、易维…...
【AI】在Ubuntu中使用docker对DeepSeek的部署与使用
这篇文章前言是我基于部署好的deepseek-r1:8b模型跑出来的 关于部署DeepSeek的前言与介绍 在当今快速发展的技术环境中,有效地利用机器学习工具来解决问题变得越来越重要。今天,我将引入一个名为DeepSeek 的工具,它作为一种强大的搜索引擎&a…...
Linux后台运行进程
linux 后台运行进程:& , nohup-腾讯云开发者社区-腾讯云 进程 &,后台运行,结束终端退出时结束进程。 nohup 进程 &,后台运行,结束终端后依然保持运行。...
unity视频在场景中的使用
(一)软件操作在平面上显示视频播放 1.创建渲染器纹理 2.创建平面 3.在平面上添加Video player 4.视频拖拽到Video player 5.渲染模式选择渲染器纹理 6.把纹理拖到目标纹理上 7.把纹理拖到平面上就可以了 然后运行项目 8.结果 (二&#…...
vue3+vite+eslint|prettier+elementplus+国际化+axios封装+pinia
文章目录 vue3 vite 创建项目如果创建项目选了 eslint prettier从零教你使用 eslint prettier第一步,下载eslint第二步,创建eslint配置文件,并下载好其他插件第三步:安装 prettier安装后配置 eslint (2025/2/7 补充) 第四步&am…...
【电商系统架构的深度剖析与技术选型】
以下是对电商系统架构的深度剖析与技术选型: 一、电商系统架构剖析 整体架构 前台系统:是用户直接交互的部分,包括用户界面、商品展示、购物车、订单结算等模块。需注重用户体验,确保页面设计美观、商品信息清晰、购物流程简便。…...
【Android】Android开发应用如何开启任务栏消息通知
Android开发应用如何开启任务栏消息通知 1. 获取通知权限2.编写通知工具类3. 进行任务栏消息通知 1. 获取通知权限 在 AndroidManifest.xml 里加上权限配置,如下。 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android…...
【Android开发AI实战】基于CNN混合YOLOV实现多车牌颜色区分且针对车牌进行矫正识别(含源码)
文章目录 引言单层卷积神经网络(Single-layer CNN)📌 单层 CNN 的基本结构📌 单层 CNN 计算流程图像 透视变换矫正车牌c实现🪄关键代码实现:🪄crnn结构图 使用jni实现高级Android开发dz…...
探索前端框架的未来:Svelte 的崛起
引言 在前端开发的世界里,框架更新换代的速度仿佛光速。从 jQuery 到 Angular,再到如今大热的 React 和 Vue,开发者们不断追逐更轻量、更快、更易于维护的框架。如今,Svelte 正悄然崛起,并引发了关于前端框架未来的热烈…...
【工具篇】深度揭秘 Midjourney:开启 AI 图像创作新时代
家人们,今天咱必须好好唠唠 Midjourney 这个在 AI 图像生成领域超火的工具!现在 AI 技术发展得那叫一个快,各种工具层出不穷,Midjourney 绝对是其中的明星产品。不管你是专业的设计师、插画师,还是像咱这种对艺术创作有点小兴趣的小白,Midjourney 都能给你带来超多惊喜,…...
多光谱成像技术在华为Mate70系列的应用
华为Mate70系列搭载了光谱技术的产物——红枫原色摄像头,这是一款150万像素的多光谱摄像头。 相较于普通摄像头,它具有以下优势: 色彩还原度高:色彩还原准确度提升约 120%,能捕捉更多光谱信息,使拍摄照片色…...
数字人|通过语音和图片来创建高质量的视频
简介 arXiv上的计算机视觉领域论文: AniPortrait: Audio-Driven Synthesis of Photorealistic Portrait Animation AniPortrait:照片级真实感肖像动画的音频驱动合成 核心内容围绕一种新的人像动画合成框架展开。 研究内容 提出 AniPortrait 框架&a…...
Vue通过触发与监听事件进行数据传递: 子组件调用 $emit 方法来将数据传递给父组件。
文章目录 引言I 组件事件事件参数defineEmits 宏声明需要抛出的事件事件校验例子:子组件告诉父组件放大所有博客文章的文字II 【详细说明】 子组件通过触发一个事件,将数据传递给父组件调用内建的 `$emit `方法传入事件名称来触发一个事件子组件通过`this.$emit`来触发一个事…...
LLMs瞬间获得视觉与听觉感知,无需专门训练:Meta的创新——在图像、音频和视频任务上实现最优性能。
引言: 问题: 当前的多模态任务(如图像、视频、音频描述生成、编辑、生成等)通常需要针对特定任务训练专门的模型,而现有的方法在跨模态泛化方面存在局限性,难以适应新任务。此外,多模态嵌入反演…...
ZZNUOJ(C/C++)基础练习1081——1090(详解版)
目录 1081 : n个数求和 (多实例测试) C C 1082 : 敲7(多实例测试) C C 1083 : 数值统计(多实例测试) C C 1084 : 计算两点间的距离(多实例测试) C C 1085 : 求奇数的乘积(多实例测试…...
Springboot实现TLS双向认证
keytool 是 Java 自带的工具,适合与 JKS 密钥库和信任库一起使用。 一、生成自签名CA证书 生成CA密钥对和自签名证书 keytool -genkeypair -alias my-ca -keyalg RSA -keysize 2048 -validity 3650 -keystore ca.jks -storepass changeit -keypass changeit -dname …...
【DeepSeek】私有化本地部署图文(Win+Mac)
目录 一、DeepSeek本地部署【Windows】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 a、直接访问 b、chatbox网页访问 二、DeepSeek本地部署【Mac】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 5、删除已下载的模型 三、DeepSeek其他 …...
深入了解 MySQL:从基础到高级特性
引言 在当今数字化时代,数据的存储和管理至关重要。MySQL 作为一款广泛使用的开源关系型数据库管理系统(RDBMS),凭借其高性能、可靠性和易用性,成为众多开发者和企业的首选。本文将详细介绍 MySQL 的基础概念、安装启…...
SQL精度丢失:CAST(ce.fund / 100 AS DECIMAL(10, 2)) 得到 99999999.99
当你使用 CAST(ce.fund / 100 AS DECIMAL(10, 2)) 进行计算并转换时得到 99999999.99 这个结果,可能由以下几种原因导致: 1. DECIMAL 类型精度限制 DECIMAL(10, 2) 表示总共可以存储 10 位数字,其中小数部分占 2 位。这意味着整数部分最多只…...
深度学习里面的而优化函数 Adam,SGD,动量法,AdaGrad 等 | PyTorch 深度学习实战
前一篇文章,使用线性回归模型逼近目标模型 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 本篇文章内容来自于 强化学习必修课:引领人工智能新时代【梗直哥瞿炜】 深度学习里面的而优化函数 …...
基于Spring Boot的图书个性化推荐系统的设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
【实战】excel分页写入导出大文件
类 RequestMapping("export")ResponseBodypublic void export(HttpServletResponse response) {long start System.currentTimeMillis();QueryVo query new QueryVo();// response响应头setResponseHeader(response, "excel");ExcelWriter writer Excel…...
