Q:

¿como repetir un struct multiples veces?

Estoy haciendo este programa que guarda informacion sobre un libro. Quiero hacerlo para que se repita las veces que desee, en este caso, 3 veces.
#include <stdio.h>

struct Libro{
    int paginas;
    const char* autor;
};

void datosLibro(struct Libro *libro[3]){
    for(int i = 0; i <= 3; i++){
        printf("paginas: ");
        scanf("%d", &libro[i]->paginas);
        printf("autor: ");
        scanf("%s", &libro[i]->autor);
    }
}

void mostrarDatos(struct Libro *libro[3]){
    for(int i = 0; i <= 3; i++){
        printf("pages: %d\n", libro[i]->paginas);
        printf("autor: %s\n", libro[i]->autor);
    }
}

int main(){
    struct Libro libro[3];

    datosLibro(&libro);
    mostrarDatos(&libro);

    return 0;
}

En primer lugar, quiero que pregunte las 3 veces sobre los datos de los libros, y finalmente la muentre toda.

A:

El programa fuente tiene varios errores:
1.- Los índices del array empiezan desde el 0 hasta N-1 (donde N es el tamaño del array, en este caso es 3), entonces, este bucle lo que ocasiona es un desbordamiento de búfer, que consiste en sobrepasarse del tamaño máximo del array, en este caso, solo se puede acceder hasta el índice 2.
for(int i = 0; i <= 3; i++)

Por lo tanto, la forma correcta de recorrer el array sería de esta forma:
for(int i = 0; i < 3; i++)

Ya que la variable i solo puede tomar valores que estén en el rango de 0 a 2.
2. La forma como accedes al array de estructuras no es la forma correcta:
libro[i]->paginas

Este encabezado:
void datosLibro(struct Libro *libro[3])

El compilador lo convierte a:
void datosLibro(struct Libro** libro)

Así es, en realidad el identificador libro es un puntero doble.
En este hilo explico la manera adecuada de acceder a un array de estructuras con un puntero doble.
Por lo tanto, en este caso deberías de acceder al array de esta forma:
(*libro + i)->paginas

3.- La forma como pides el nombre del autor al usuario no está correcto:
scanf("%s", &libro[i]->autor);

Cuando leemos una cadena por teclado, le debes pasar a la función scanf la dirección base de un array de caracteres, en este caso, no lo haces, por lo tanto, ocurrirá un fallo de segmentación, ya que la función scanf intentará escribir en áreas de memoria que al programa no le pertenece. Todo esto se debe porque estás pasando la dirección de memoria de un puntero, que en realidad no apunta a ningún array.
Aquí tenemos dos posibles soluciones:
A) Reservamos un array dinámico de X elementos con la función malloc. De este modo, el puntero autor apuntará a una región de memoria donde el programa si pueda acceder. Al final solo necesitaríamos pasar el contenido del puntero a la función scanf. En esta solución se debe liberar la memoria manualmente con la función free.
Ejemplo:
//Reservamos memoria con malloc
(*libro + i)->autor = malloc(sizeof(char) * 20);
scanf("%s", (*libro + i)->autor);

B) Cambiar la declaración del miembro autor a:
struct Libro{
    int paginas;
    char autor[20];
};

En este caso reservamos memoria para un array de 20 elementos. Esta solución es más fácil porque nos evita usar memoria dinámica y liberar la memoria.
Ejemplo:
//Aquí pasamos la dirección base del *array*
scanf("%s", (*libro + i)->autor);

Aquí no hace falta el ampersand, debido a que, autor representa la dirección base del array.
4.- No estamos pasando correctamente los argumentos a cada parámetro:
datosLibro(&libro);
mostrarDatos(&libro);

Ya sabemos que el parámetro libro es convertido de manera implicita a un puntero doble, pero en ningún momento pasamos la dirección de memoria de un puntero, eso es lo que realmente debe recibir un puntero doble.
Solución: Te toca declarar un puntero simple adicional y pasarlo como argumento:
int main()
{
    struct Libro libro[3];
    struct Libro* p = &libro[0];
    datosLibro(&p);
    mostrarDatos(&p);
    return 0;
}

El código completo quedaría así:
#include <stdio.h>

struct Libro
{
    int paginas;
    char autor[20];
};

void datosLibro(struct Libro* libro[])
{
    for(int i = 0; i < 3; i++)
    {
        printf("paginas: ");
        scanf("%d", &(*libro + i)->paginas);
        printf("autor: ");
        scanf("%s", (*libro + i)->autor);
    }
}

void mostrarDatos(struct Libro* libro[])
{
    for(int i = 0; i < 3; i++)
    {
        printf("pages: %d\n", (*libro + i)->paginas);
        printf("autor: %s\n", (*libro + i)->autor);
    }
}  

int main()
{
    struct Libro libro[3];
    struct Libro* p = &libro[0];
    datosLibro(&p);
    mostrarDatos(&p);
    return 0;
}

A pesar que el anterior código resuelve el problema, prefiero no trabajar con punteros dobles, sino, con simples. Por lo tanto, debemos cambiar la declaración de cada parámetro a un puntero simple:
#include <stdio.h>

struct Libro
{
    int paginas;
    char autor[20];
};

void datosLibro(struct Libro* libro)
{
    for(int i = 0; i < 3; i++)
    {
        printf("paginas: ");
        scanf("%d", &libro[i].paginas);
        printf("autor: ");
        scanf("%s", libro[i].autor);
    }
}

void mostrarDatos(struct Libro*libro)
{
    for(int i = 0; i < 3; i++){
        printf("pages: %d\n", libro[i].paginas);
        printf("autor: %s\n", libro[i].autor);
    }
}  

int main()
{
    struct Libro libro[3];
    datosLibro(&libro[0]);
    mostrarDatos(&libro[0]);
    return 0;
}

Observación:
Si vas a pedir una cadena con la función scanf, no olvides de especificar el tamaño del array para que no ocurra un desbordamiento de búfer:
//Es 19, porque se debe dejar un espacio para el caracter nulo.
 scanf("%19s", libro[i].autor);

De este modo, si el usuario ingresa más de 19 caracteres, la función scanf solo leerá hasta 19.

