2025-03-28 12:31:51 -03:00
|
|
|
#+title: Curso Básico da Linguagem C
|
|
|
|
#+subtitle: Aula 7: Vetores, ponteiros e strings
|
|
|
|
#+author: Blau Araujo
|
|
|
|
#+startup: show2levels
|
|
|
|
#+options: toc:3
|
|
|
|
|
|
|
|
* Aula 7: Vetores, ponteiros e strings
|
|
|
|
|
|
|
|
[[https://youtu.be/hhySl3ClTLE][Vídeo desta aula]]
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
Parte do conteúdo desta aula foi antecipado nas [[../06-vetores/README.org][anotações da aula 6]]!
|
|
|
|
#+end_quote
|
|
|
|
|
|
|
|
Embora sejam conceitos totalmente distintos, existem relações importantes entre
|
|
|
|
vetores e ponteiros que nós precisamos conhecer.
|
|
|
|
|
|
|
|
** Notações de acesso
|
|
|
|
|
|
|
|
Tanto ponteiros quanto vetores (e seus elementos) podem ser acessados de
|
|
|
|
duas formas...
|
|
|
|
|
|
|
|
| Acesso | Aritmética | Subscrito |
|
|
|
|
|----------------+------------------+---------------|
|
|
|
|
| Valor/Elemento | =*(nome + índice)= | =nome[índice]= |
|
|
|
|
| Endereço | =nome + índice= | =&nome[índice]= |
|
|
|
|
|
|
|
|
*** Exemplo
|
|
|
|
|
|
|
|
*Dados o vetor e o ponteiro abaixo...*
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
char v[] = {65, 66, 67, 68, 69, 0}; // Vetor de caracteres (bytes)
|
|
|
|
char *ptr = "FGHIJ"; // Ponteiro para uma string em .rodata (read only)
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
*Impressão do endereço de um caractere...*
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
// Impressões do vetor...
|
|
|
|
printf("v[2] : %c (%d) --> &v[2]: %p\n", v[2], v[2], &v[2]);
|
|
|
|
printf("*(v + 2): %c (%d) --> v + 2: %p\n", *(v + 2), *(v + 2), v + 2);
|
|
|
|
|
|
|
|
// Impressões do ponteiro...
|
|
|
|
printf("p[2] : %c (%d) --> &p[2]: %p\n", p[2], p[2], &p[2]);
|
|
|
|
printf("*(p + 2): %c (%d) --> p + 2: %p\n", *(p + 2), *(p + 2), p + 2);
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
*Saída do programa...*
|
|
|
|
|
|
|
|
#+begin_example
|
|
|
|
v[2] : C (67) --> &v[2]: 0x7ffda53a16c4
|
|
|
|
*(v + 2): C (67) --> v + 2: 0x7ffda53a16c4
|
|
|
|
p[2] : H (72) --> &p[2]: 0x562d0c93100a
|
|
|
|
*(p + 2): H (72) --> p + 2: 0x562d0c93100a
|
|
|
|
#+end_example
|
|
|
|
|
|
|
|
** Vetores e funções
|
|
|
|
|
|
|
|
Observe esta função:
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
int array_sum(int arr[], int size) {
|
|
|
|
int sum = 0;
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
sum += arr[i]; // arr é um ponteiro, não um vetor!
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
- Funções não podem receber vetores como argumentos, apenas seus endereços.
|
|
|
|
- Escreve-se =int arr[]= apenas para indicar que o argumento esperado é o
|
|
|
|
endereço de um vetor, já que =arr= será compilado como um ponteiro.
|
|
|
|
- Do mesmo modo, utiliza-se =arr[i]=, na iteração, para manter a coerência
|
|
|
|
semântica, já que a notação =nome[índice]= também pode ser utilizada com
|
|
|
|
ponteiros.
|
|
|
|
|
|
|
|
Este mesmo exemplo poderia ser escrito apenas com notações de ponteiros
|
|
|
|
e aritmética de ponteiros:
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
int array_sum(int *arr, int size) {
|
|
|
|
int sum = 0;
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
sum += *(arr + i); // Notação aritmética!
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Em ambos os casos, a função lidará com o ponteiro (=arr=) que recebeu como
|
|
|
|
argumento o endereço de um vetor. O vetor em si, jamais será convertido
|
|
|
|
em ponteiro e nem tampouco /"decairá para um ponteiro"/!
|
|
|
|
|
|
|
|
** Strings são vetores de caracteres
|
|
|
|
|
|
|
|
Strings são vetores de caracteres terminados com o caractere nulo (=\0=).
|
|
|
|
|
|
|
|
*Declaração de um vetor para receber uma string...*
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
char str[TAMANHO];
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Quando o vetor é declarado sem ser inicializado, é obrigatório definir
|
|
|
|
a quantidade de elementos que o vetor receberá.
|
|
|
|
|
|
|
|
*Declaração e inicialização de um vetor com uma string...*
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
char str[] = {'b', 'a', 'c', 'a', 'n', 'a', '\0'};
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Quando o vetor é inicializado, a quantidade de elementos pode ser
|
|
|
|
omitida para ser definida na compilação.
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
A presença da terminação do caractere terminador ='\0'= é que define
|
|
|
|
uma cadeia de caracteres como uma string, ou seja: uma cadeia de bytes
|
|
|
|
que pode ser utilizada com segurança em funções que procuram o terminador
|
|
|
|
para determinar o fim do processamento da cadeia de caracteres.
|
|
|
|
#+end_quote
|
|
|
|
|
|
|
|
*Declaração e inicialização de um vetor com uma string literal...*
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
char str[] = "bacana";
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Quando a string literal ="bacana"= é utilizada na inicialização do vetor,
|
|
|
|
o compilador copia cada um de seus bytes para o vetor e inclui o terminador
|
|
|
|
nulo ('\0') como último elemento.
|
|
|
|
|
|
|
|
*Inicialização de um ponteiro com uma string literal...*
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
char *ptr = "cabana";
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Ponteiros são variáveis que recebem endereços como argumentos. Sendo assim,
|
|
|
|
o compilador copia os bytes da string literal ="cabana"= e inclui o terminador
|
|
|
|
nulo na seção =.rodata= (/read only data/) como um vetor de caracteres de 7
|
|
|
|
elementos e passa o endereço do primeiro byte para o ponteiro.
|
|
|
|
|
|
|
|
#+begin_quote
|
|
|
|
O endereço de uma string, que é um vetor, é o endereço de seu primeiro byte.
|
|
|
|
#+end_quote
|
|
|
|
|
|
|
|
** Inicializadores escalares e agregados
|
|
|
|
|
|
|
|
Ponteiros são variáveis e, portanto, requerem /inicializadores escalares/: no
|
|
|
|
caso, especificamente endereços.
|
|
|
|
|
|
|
|
Todos esses dados são escalares e podem inicializar variáveis:
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
// Variável inicializada com o valor '10' (expressão constante)...
|
|
|
|
int a = 10;
|
|
|
|
|
|
|
|
// Ponteiro inicializado com valor '&a' (endereço de 'a')...
|
|
|
|
int *pa = &a;
|
|
|
|
|
|
|
|
// Ponteiro inicializado com o endereço do primeiro byte de uma string...
|
|
|
|
char *ps = "uma string literal";
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Vetores, por sua vez, são estruturas que agrupam vários dados de mesmo tipo e,
|
|
|
|
por isso, requerem /inicializadores agregados/.
|
|
|
|
|
|
|
|
Todos esses dados são agregados de valores e podem inicializar vetores...
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
// Vetor inicializado com uma lista de 5 valores...
|
|
|
|
int n[5] = {1, 2, 3, 4, 5};
|
|
|
|
|
|
|
|
// Vetor inicializado com 5 valores '0'...
|
|
|
|
int z[5] = {0};
|
|
|
|
|
|
|
|
// Vetor inicializado com 5 caracteres (bytes)...
|
|
|
|
char c[] = {'a', 'b', 'c', 'd', '\0'}; // Quantidade de elementos definida na compilação...
|
|
|
|
|
|
|
|
// Vetor inicializado com 7 caracteres (bytes)...
|
|
|
|
char s[] = "qwerty"; // O sétimo byte é o caractere nulo (\0 == 0x00)...
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Mas, repare nessas linhas...
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
char *ps = "uma string literal";
|
|
|
|
char s[] = "qwerty";
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
As strings literais são dados agregados do tipo =char *=, mas podem ser
|
|
|
|
utilizadas para inicializar tanto variáveis quanto vetores: tudo depende
|
|
|
|
do contexto...
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
long a = (long)"uma string"; // Endereço da string modelado para long int.
|
|
|
|
|
|
|
|
char *p = "outra string"; // O endereço da string é do tipo 'char[N bytes]'.
|
|
|
|
|
|
|
|
char s[] = "mais uma"; // O endereço da string será o endereço do vetor,
|
|
|
|
// que só recebe os caracteres como elementos.
|
|
|
|
|
|
|
|
char f[] = {'o', 'i', '\0'}; // Equivale e inicializar com "oi".
|
|
|
|
#+end_src
|
|
|
|
|
2025-03-29 06:02:41 -03:00
|
|
|
*Notas:*
|
2025-03-28 12:31:51 -03:00
|
|
|
|
2025-03-29 06:02:41 -03:00
|
|
|
- Por ser uma estrutura de dados, o "tipo" de um vetor é frequentemente descrito
|
|
|
|
como =TIPO[TAMANHO]=, mas isso não é um tipo da linguagem C -- é apenas uma
|
|
|
|
forma utilizada para expressar a ideia de um dado que é composto por uma certa
|
|
|
|
quantidade de elementos de um determinado tipo. Sendo um vetor de caracteres,
|
|
|
|
uma string literal na memória, por exemplo, seria descrita como =char[TAMANHO]=.
|
|
|
|
|
|
|
|
- Quando atribuída a um elemento escalar, como uma variável, um ponteiro ou o
|
|
|
|
argumento de uma função, a string literal expressa o endereço de seu primeiro
|
|
|
|
byte, ou seja, o endereço do primeiro elemento de um vetor do tipo =char=.
|
|
|
|
|
|
|
|
** Vetores de comprimentos variáveis (VLA)
|
|
|
|
|
|
|
|
A alocação do espaço ocupado por um vetor é feita estaticamente em tempo de
|
|
|
|
compilação, o que nos impediria de declarar e inicializar vetores com uma
|
|
|
|
quantidade de elementos obtida pela avaliação de uma variável ou pelo retorno
|
|
|
|
de uma função, por exemplo: isso é chamado de VLA (/variable-length array/).
|
|
|
|
|
|
|
|
O GCC, porém, implementa o suporte a VLA na forma de uma extensão habilitada por
|
|
|
|
padrão, possibilitando a declaração de vetores com número de elementos obtidos
|
|
|
|
dinamicamente, mas não a sua inicialização:
|
|
|
|
|
|
|
|
#+begin_src c
|
|
|
|
int size = 10;
|
|
|
|
|
|
|
|
int a[size]; // Permitido no GCC...
|
|
|
|
int v[size] = {0}; // Proibido!
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
Outros compiladores podem não suportar VLAs:
|
|
|
|
|
|
|
|
| Compilador | Suporte a VLAs | Observações |
|
|
|
|
|-----------------------+----------------+------------------------------|
|
|
|
|
| *MSVC (cl.exe)* | Não suporta | Gera erro de compilação |
|
|
|
|
| *GCC (Linux/Mingw-w64)* | Suporta | VLAs alocados na pilha |
|
|
|
|
| *Clang (MinGW/WSL)* | Suporta | Mesmo comportamento do Linux |
|
|
|
|
| *Clang-cl (modo MSVC)* | Não suporta | Igual ao MSVC |
|