Хорошо, я надеюсь, что меня не побьют с какой-либо небольшой ошибкой ... вот моя попытка объяснить это настолько полно, насколько смогу.
С обычным символом * он будет указывать на символ.
с символом ** указывает на указатель на символ. Это значение * k находится в куче, а не в стеке.
т.е. как это:
stack (1) heap (2) heap or ... (3)
+-----+ +-----+ +----+
|char*| -> |char*| -> |char|
+-----+ +-----+ +----+
Теперь символы * на самом деле не являются строками, но они обрабатываются как блоки непрерывных печатаемых символов в памяти, которые заканчиваются нулевым или нулевым байтом. Таким образом, строка будет сохранена и на нее будут ссылаться в (3)
Таким образом, чтобы исправить ваш код, вам нужно выделить место для символа * (не символа).
т.е. ставить
k = (char**)malloc(sizeof(char*));
перед строкой
*k = s;
Не то чтобы это хороший код, но он не должен вылетать.