Cuando escribe una "cadena" en su código fuente, se escribe directamente en el ejecutable porque ese valor debe conocerse en el momento de la compilación (hay herramientas disponibles para separar el software y encontrar todas las cadenas de texto sin formato). Cuando escribe char *a = "This is a string", la ubicación de "Esto es una cadena" está en el ejecutable, y la ubicación a la que aapunta, está en el ejecutable. Los datos de la imagen ejecutable son de solo lectura.
Lo que debe hacer (como han señalado las otras respuestas) es crear esa memoria en una ubicación que no sea de solo lectura, en el montón o en el marco de la pila. Si declara una matriz local, se crea espacio en la pila para cada elemento de esa matriz, y el literal de cadena (que se almacena en el ejecutable) se copia en ese espacio en la pila.
char a[] = "This is a string";
también puede copiar esos datos manualmente asignando algo de memoria en el montón y luego usar strcpy()para copiar un literal de cadena en ese espacio.
char *a = malloc(256);
strcpy(a, "This is a string");
Siempre que asigne espacio usando malloc()recuerde llamar free()cuando haya terminado (lea: pérdida de memoria).
Básicamente, debes realizar un seguimiento de dónde están tus datos. Cada vez que escribe una cadena en su fuente, esa cadena es de solo lectura (de lo contrario, estaría cambiando potencialmente el comportamiento del ejecutable; imagínese si escribió char *a = "hello";y luego cambió a[0]a 'c'. Luego, en otro lugar escribió printf("hello");. Si se le permitiera cambiar el primero carácter de "hello", y su compilador solo lo almacenó una vez (debería), ¡luego printf("hello");saldría cello!